14 / 90

學習日 14:一次性儲存庫中的 Git 協作

在不觸碰課程工作樹的情況下使用分支、差異、衝突、安全復原和 PR 式評審。

自主學習與 Python 480 分鐘(8 小時專注學習)
01 120 分鐘

分支、差異與評審

代理協助為可選,須遵循書面計畫;學習者擁有權限並驗證每一處差異與命令結果。若所選代理不可用,協助部分記為 blocked/unexecuted——絕不模擬。

先決條件

第 13 天的專案產物和基本的 Git status/log/diff。

學習目標

建立一個一次性複製,對文字專案做一處聚焦的分支修改,並在提交前評審差異。

操作說明

必做:按參考答案的步驟建立私有基礎儲存庫與複製(只把 projects/text_tools/{__init__.py,core.py,cli.py} 複製進新的私有基礎儲存庫——絕不可複製、發布或改動學習儲存庫及其 .git、資料庫、.venv 或密鑰),然後建立分支 parser-help,完成指定的 --input 說明單行改進,並在一個聚焦提交之前記錄 branch、status、log、staged 與 unstaged 差異以及 git diff --check。用公開的 --help 檢查證明行為——差異加結束碼 0 是證據而非證明。每個區塊都在帶 set -e 的 ( ... ) 內執行:先驗證新建的 WORKDIR 再使用,mkdir/cp/cd 失敗即在子 shell 內中止——失敗的命令絕不會「漏過去」在你目前的儲存庫裡執行 Git,export 也不會洩漏到呼叫者 shell。d14env 用 env -i 包裹外部命令,丟棄環境中的 GIT_DIR/GIT_WORK_TREE/index/object/alternates、GIT_CONFIG*、XDG、範本/鉤子/簽章變數,使它們無法操縱「這些作例命令」;但僅靠 env -i 並不能阻止命令寫到 WORKDIR 之外——讓作例命令留在 WORKDIR 內的是已驗證的前提條件與 fail-stop。PATH 只決定執行哪些執行檔(會印出解析後的路徑;Python 固定為絕對路徑 PYTHON_BIN)——它不是可信工具清單,也不是檔案系統邊界。這是環境衛生而非 OS 沙箱。列印出的 WORKDIR 值由你自己 export——後續活動區塊要求它。選做:可按書面計畫讓真實代理評審差異;若所選工具不可用,將該部分記為 blocked/unexecuted,絕不偽造會話記錄。

練習與預測

預測 --input 說明改進會改變 projects/text_tools/cli.py 的哪一行,以及複製的三個檔案中哪些必須保持位元組一致(用 sha256sum 驗證)。解釋為什麼 git diff 加零結束碼是證據而不是行為的證明,以及當 GIT_DIR 可能指向別的儲存庫時為何必須經 d14env(env -i + 私有 HOME/XDG/config/template + 關閉簽章)執行,以及為何 ( set -e 的 fail-stop 與 WORKDIR 驗證能防止失敗的 cd 在呼叫者儲存庫中執行 Git。執行以下完整的私有環境與檢查:

(
set -e
WORKDIR=$(mktemp -d)
[ -n "$WORKDIR" ] && [ -d "$WORKDIR" ] && [ -w "$WORKDIR" ] && [ -x "$WORKDIR" ] || {
  echo "abort: WORKDIR '$WORKDIR' is missing or unusable - refusing to run anything outside it" >&2
  exit 1
}
LEARN=/absolute/path/to/your/clone
[ -f "$LEARN/projects/text_tools/cli.py" ] || {
  echo "abort: LEARN must point at a checkout containing projects/text_tools/cli.py" >&2
  exit 1
}
export WORKDIR LEARN
mkdir -p "$WORKDIR/home" "$WORKDIR/xdg" "$WORKDIR/tpl" "$WORKDIR/tmp"
d14env() {
  env -i PATH="$PATH" HOME="$WORKDIR/home" XDG_CONFIG_HOME="$WORKDIR/xdg" \
      TMPDIR="$WORKDIR/tmp" WORKDIR="$WORKDIR" LEARN="$LEARN" \
      GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL=/dev/null \
      GIT_TEMPLATE_DIR="$WORKDIR/tpl" \
      GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=commit.gpgsign GIT_CONFIG_VALUE_0=false \
      LC_ALL=C PYTHONDONTWRITEBYTECODE=1 "$@"
}
command -v git sed python3
PYTHON_BIN="$(command -v python3)"
mkdir -p "$WORKDIR/base/projects/text_tools"
cp "$LEARN"/projects/text_tools/__init__.py "$LEARN"/projects/text_tools/core.py "$LEARN"/projects/text_tools/cli.py "$WORKDIR/base/projects/text_tools/"
cd "$WORKDIR/base"
d14env git init
d14env git config user.name "Learner"
d14env git config user.email "[email protected]"
d14env sha256sum projects/text_tools/*.py
d14env git add projects/
d14env git commit -m "text_tools snapshot"
d14env git clone "$WORKDIR/base" "$WORKDIR/clone"
cd "$WORKDIR/clone"
d14env git config user.name "Learner"
d14env git config user.email "[email protected]"
d14env git switch -c parser-help
d14env sed -i 's|corpus file\.|corpus file (UTF-8 by default; override with --encoding/--errors).|' projects/text_tools/cli.py
d14env git status --short
d14env git diff
d14env git diff --cached
d14env git diff --check
d14env git add projects/text_tools/cli.py
d14env git commit -m "cli: document --input decoding defaults"
d14env git log --oneline -3
d14env "$PYTHON_BIN" projects/text_tools/cli.py --help | grep -F "UTF-8 by default"
printf 'export WORKDIR=%s  # run this before the later activity blocks\n' "$WORKDIR"
)

Learning Project Submission & Evidence

0 / 4096 位元組
Declared Assistance Condition

Current Submitted Evidence Details

No submission recorded yet.
獨立掌握標準

學習者能附上命令輸出講清分支、提交、差異與評審的邊界——包括證明新說明文字的公開 --help 檢查——並說明可選的代理評審是已執行還是如實記為 blocked/unexecuted。

來源資源與出處

linking CC BY-NC-SA 3.0 noncommercial share-alike

Pro Git, Second Edition

作者 / 機構: Scott Chacon and Ben Straub | 原始出處連結 ↗

對應章節 / 時間戳: Chapter 3 (Git Branching: branches, basic merging and conflicts), Chapter 7.7 (Reset Demystified), Chapter 5.2 (Contributing to a Project)

驗證版本與日期: Second edition (Apress, 2014); verified 2026-10-30

入選理由:

Freely licensed reference for branching, merging, conflict resolution, and safe recovery vocabulary.

來源前置條件:

Basic git status/log/diff vocabulary from earlier study days

安全回退方案(離線彈性):

git help <command> and the Git manual pages ship with Git and cover the same commands offline; every required workflow is stated locally in the lesson.

對比候選資源與選型分析 (2)
GitHub Skills interactive courses ↗ compared_rejected_for_day14 [MIT (varies by course)]

Interactive but requires a GitHub account and an external service; disposable local repositories keep the exercise self-contained.

Atlassian Git Tutorials ↗ compared_rejected_for_day14 [All Rights Reserved]

Vendor-owned prose with no permissive license; Pro Git covers the same ground under CC BY-NC-SA.

02 120 分鐘

獨立解決衝突

獨立掌握檢查:不允許 AI 或抄來的衝突解決;可查閱官方 Git 文件。

先決條件

一個有兩條分叉分支的一次性儲存庫。

學習目標

獨立解決真實的 text_tools 夾具衝突,同時保留雙方預期的修改。

操作說明

必做:先獨立嘗試一個全新的變體——選 cli.py 中另一行真實文字(例如 --seed 說明文字)和你自己想出的兩個相容需求詞,建立兩條分叉分支,合併,做保留兩個用語的解決,並用兩次獨立的 --help 檢查加 git rev-list --parents -n 1 HEAD(三個 id = 雙親合併)驗證。然後與下方使用 description 行和 Unicode-aware、original-byte 兩詞的參考案例對照。本區塊複用環境區塊匯出的 WORKDIR 夾具(或先把它列印出的路徑設為 WORKDIR),並重新宣告 d14env。可查閱官方 Git 文件;AI 輸出或抄來的解決不計入獨立掌握。選做:事後對照參考講解。

練習與預測

在合併前預測你的變體會出現的衝突標記和最終行內容。解釋為什麼盲目選擇 ours 或 theirs 會遺失需求,以及解決如何同時滿足兩條驗收條件。參考序列(同一私有複製):

(
set -e
: "${WORKDIR:?set WORKDIR to the private Day 14 fixture path first}"
[ -d "$WORKDIR/clone/.git" ] || { echo "private clone not found - run the setup block first"; exit 1; }
d14env() {
  env -i PATH="$PATH" HOME="$WORKDIR/home" XDG_CONFIG_HOME="$WORKDIR/xdg" \
      TMPDIR="$WORKDIR/tmp" WORKDIR="$WORKDIR" LEARN="$LEARN" \
      GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL=/dev/null \
      GIT_TEMPLATE_DIR="$WORKDIR/tpl" \
      GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=commit.gpgsign GIT_CONFIG_VALUE_0=false \
      LC_ALL=C PYTHONDONTWRITEBYTECODE=1 "$@"
}
command -v git sed python3
PYTHON_BIN="$(command -v python3)"
cd "$WORKDIR/clone"
d14env git switch -c desc-unicode
d14env sed -i 's|compute statistics, deterministic splits, and provenance artifacts\.|compute Unicode-aware statistics, deterministic splits, and provenance artifacts.|' projects/text_tools/cli.py
d14env git commit -am "describe Unicode-aware statistics"
d14env git switch -
d14env git switch -c desc-provenance
d14env sed -i 's|and provenance artifacts\.|and original-byte provenance artifacts.|' projects/text_tools/cli.py
d14env git commit -am "describe original-byte provenance"
if d14env git merge desc-unicode; then
  echo "unexpected: merge resolved cleanly" >&2
  exit 1
else
  echo "expected non-zero exit (content conflict): $?"
fi
d14env sed -n '/<<<<<<</,/>>>>>>>/p' projects/text_tools/cli.py
d14env "$PYTHON_BIN" - <<'PY'
from pathlib import Path
p = Path('projects/text_tools/cli.py')
s = p.read_text()
i = s.index('<<<<<<<')
j = s.index('\n', s.index('>>>>>>>', i))
resolved = '        description="Text and Data Tools: compute Unicode-aware statistics, deterministic splits, and original-byte provenance artifacts."\n'
p.write_text(s[:i] + resolved + s[j+1:])
PY
d14env git add projects/text_tools/cli.py
d14env git commit -m "merge: keep both requirements"
d14env git rev-list --parents -n 1 HEAD
d14env "$PYTHON_BIN" projects/text_tools/cli.py --help | grep -F "Unicode-aware"
d14env "$PYTHON_BIN" projects/text_tools/cli.py --help | grep -F "original-byte"
d14env git status --short
)
獨立掌握標準

學習者自己的變體上無協助的解決正確:兩個需求詞經 --help 驗證、雙親合併提交被記錄,證據指明衝突、所選行為、檢查與安全邊界。

來源資源與出處

linking CC BY-NC-SA 3.0 noncommercial share-alike

Pro Git, Second Edition

作者 / 機構: Scott Chacon and Ben Straub | 原始出處連結 ↗

對應章節 / 時間戳: Section 3.2 (Basic Merging and Conflicts)

驗證版本與日期: Second edition (Apress, 2014); verified 2026-10-30

入選理由:

Standard reference for conflict markers and merge semantics underlying the independent resolution exercise.

來源前置條件:

Completion of day-14-act-01 in a disposable repository

安全回退方案(離線彈性):

git help merge documents conflict handling offline; the disposable fixture and instructions are fully local.

對比候選資源與選型分析 (2)
Oh Shit, Git!?! ↗ compared_alternative [Copyright Katie Sylor-Miller (all rights reserved; linking only)]

Strong recovery cheatsheet, but conflict resolution is exercised hands-on; Pro Git remains the primary reference.

Learn Git Branching ↗ compared_alternative [MIT]

Visual branch/merge sandbox; useful companion but the exercise must run in a real disposable repo.

03 120 分鐘

安全復原

代理協助的工程檢查:代理可以解釋選項,但由學習者執行並驗證每一步;工具不可用時記為 blocked/unexecuted。

先決條件

一個已提交的一次性夾具和一個故意的、可逆的錯誤。

學習目標

使用安全的、非破壞性操作,從誤編輯或不想要的提交中復原。

操作說明

必做(同一私有複製):(0) 建立正典參考基線——從 parser-help 切出 d14-ref 分支並提交已解決的 description 行;你已封存的獨立嘗試留在自己的分支上、絕不丟棄。(A) 先備份 cli.py,重現兩塊 hunk 的未暫存情形(description 行 MISTAKE 拼字錯誤 + --seed 說明行 KEEP 編輯),檢視 git diff,只復原拼字錯誤並驗證 KEEP hunk 仍在,然後把該有效編輯提交——已提交的修改不會被後面的清理演示丟棄。(B) 用一個新的單 hunk 錯誤演示暫存區與工作樹的區別:暫存它,展示 git restore <path> 會把它留在索引中,再 --staged 取消暫存並做整路徑捨棄——此時僅剩的改動確實不要,所以是正當的;驗證工作樹乾淨且已提交的 KEEP 在 --help 中仍生效。(C) 先提交已知的單親 BAD 提交,再提交獨立的 GOOD 提交,用新提交 revert BAD,證明 GOOD 與 KEEP 都保留且歷史未被改寫;最後(預期失敗)觀察對合併提交 revert 需要 -m 指定主線。選做:執行後與代理複盤復原方式的選擇。

練習與預測

對與有效編輯同處一檔案的未暫存拼字錯誤,以及跟在單親壞提交之後的獨立良好提交:分別選擇 git restore、git revert 或保存的副本,並在執行前預測最終的差異與歷史。為什麼 KEEP 編輯必須在演示暫存錯誤之前提交,而不僅僅在工作樹中倖存?

(
set -e
: "${WORKDIR:?set WORKDIR to the private Day 14 fixture path first}"
[ -d "$WORKDIR/clone/.git" ] || { echo "private clone not found - run the setup block first"; exit 1; }
d14env() {
  env -i PATH="$PATH" HOME="$WORKDIR/home" XDG_CONFIG_HOME="$WORKDIR/xdg" \
      TMPDIR="$WORKDIR/tmp" WORKDIR="$WORKDIR" LEARN="$LEARN" \
      GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL=/dev/null \
      GIT_TEMPLATE_DIR="$WORKDIR/tpl" \
      GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=commit.gpgsign GIT_CONFIG_VALUE_0=false \
      LC_ALL=C PYTHONDONTWRITEBYTECODE=1 "$@"
}
command -v git sed python3
PYTHON_BIN="$(command -v python3)"
cd "$WORKDIR/clone"
d14env git switch -c d14-ref parser-help
d14env sed -i 's|compute statistics, deterministic splits, and provenance artifacts\.|compute Unicode-aware statistics, deterministic splits, and original-byte provenance artifacts.|' projects/text_tools/cli.py
d14env git commit -am "reference baseline: description keeps both requirement terms"
d14env sed -i 's|Unicode-aware statistics|statistisc|' projects/text_tools/cli.py
d14env sed -i 's|(default: 42)\.|(default: 42; any integer).|' projects/text_tools/cli.py
d14env cp projects/text_tools/cli.py "$WORKDIR/cli-backup.py"
d14env git diff
d14env sed -i 's|statistisc|Unicode-aware statistics|' projects/text_tools/cli.py
d14env git diff
d14env git diff --check
d14env "$PYTHON_BIN" projects/text_tools/cli.py --help | grep -F "any integer"
d14env git commit -am "document that --seed accepts any integer"
d14env sed -i 's|default=42,|default=7,|' projects/text_tools/cli.py
d14env git add projects/text_tools/cli.py
d14env git status --short
d14env git restore projects/text_tools/cli.py
d14env bash -c 'git diff --cached | grep -F "default=7"'
d14env git restore --staged projects/text_tools/cli.py
d14env git restore projects/text_tools/cli.py
d14env git status --short
d14env bash -c 'test -z "$(git status --porcelain)"'
d14env "$PYTHON_BIN" projects/text_tools/cli.py --help | grep -F "any integer"
d14env sed -i 's|default=42,|default=7,|' projects/text_tools/cli.py
d14env git commit -am "BAD: change default seed to 7"
BAD=$(d14env git rev-parse HEAD)
d14env sed -i 's|(default: 0.1)\.|(default: 0.1; must be in [0, 1]).|' projects/text_tools/cli.py
d14env git commit -am "GOOD: clarify split-ratio range"
d14env git revert --no-edit "$BAD"
d14env grep -F "default=42," projects/text_tools/cli.py
d14env grep -F "must be in [0, 1]" projects/text_tools/cli.py
d14env grep -F "any integer" projects/text_tools/cli.py
d14env git log --oneline -3
d14env git rev-list --count HEAD
MERGE_SHA=$(d14env git rev-list --all --merges -n 1)
[ -n "$MERGE_SHA" ] || { echo "no merge commit found - run the act-02 reference merge first" >&2; exit 1; }
if d14env git revert "$MERGE_SHA" 2>"$WORKDIR/merge_revert.err"; then
  echo "unexpected: merge revert succeeded without -m" >&2
  exit 1
else
  rc=$?
  grep -F "no -m option" "$WORKDIR/merge_revert.err" && echo "expected non-zero exit (merge needs -m): $rc"
fi
)
獨立掌握標準

學習者能在不丟棄已封存工作的情況下建立正典基線,根據儲存庫狀態論證每個操作,透過 KEEP 自身的提交加乾淨的 status 與 --help 檢查證明其在清理後仍然保留,展示 revert 以新提交而非改寫歷史的方式生效,並在不猜測主線的情況下觀察到合併 revert 的失敗。

來源資源與出處

linking CC BY-NC-SA 3.0 noncommercial share-alike

Pro Git, Second Edition

作者 / 機構: Scott Chacon and Ben Straub | 原始出處連結 ↗

對應章節 / 時間戳: Section 7.7 (Reset Demystified) and Section 2.4 (Undoing Things): restore, revert, and history boundaries

驗證版本與日期: Second edition (Apress, 2014); verified 2026-10-30

入選理由:

Freely licensed explanation of why restore and revert are the safe recovery tools while destructive commands are deferred.

來源前置條件:

Completion of day-14-act-02

安全回退方案(離線彈性):

git help restore and git help revert document the same safe operations offline.

對比候選資源與選型分析 (2)
Git documentation: git-restore/git-revert ↗ compared_alternative [GPL-2.0 (Git) / CC BY-NC-SA 3.0 (book-derived pages)]

Exact command references, useful as a companion; Pro Git provides the conceptual model the activity teaches.

Oh Shit, Git!?! ↗ compared_alternative [Copyright Katie Sylor-Miller (all rights reserved; linking only)]

Recovery cheatsheet matching the safe-recovery theme; Pro Git provides the underlying model.

04 120 分鐘

PR 式評審與檢索

獨立掌握檢查:預測、自己的檢查和封存評審均無協助;之後的協助對比是獨立部分,絕不替代封存稿。

先決條件

已完成的分支、衝突和復原證據。

學習目標

像評審拉取請求一樣評審一次性改動,區分正確性、範圍、測試、風險和證據。

操作說明

必做:先無協助地記錄你的預測/初始假設;然後親自執行弱檢查和強檢查——自己的執行屬於無協助嘗試的一部分,不算協助;再撰寫並封存(提交、註明日期)完成的無協助評審——驗收標準、阻塞性意見、非阻塞性意見、實際觀察到的證據和邊界。若行為其實正確,封存評審仍要記錄所跑檢查及充分理由——只寫「看起來沒問題」為不合格。選做:之後可對照真實的代理評審並標註 assisted(不替代封存稿);任何借助協助的修正都要做一次新的無協助複查。不授權對外發布。

練習與預測

在執行強檢查前,預測 --help 列出 --quiet 是否證明該旗標生效。指出弱冒煙檢查看不到的缺陷,以及能暴露它的更強 CLI 檢查。修補程式(評審者編寫,非觀察到的代理輸出):

(
set -e
: "${WORKDIR:?set WORKDIR to your private Day 14 fixture path first}"
[ -d "$WORKDIR/clone/.git" ] || { echo "private clone not found - run the setup block first"; exit 1; }
d14env() {
  env -i PATH="$PATH" HOME="$WORKDIR/home" XDG_CONFIG_HOME="$WORKDIR/xdg" \
      TMPDIR="$WORKDIR/tmp" WORKDIR="$WORKDIR" LEARN="$LEARN" \
      GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL=/dev/null \
      GIT_TEMPLATE_DIR="$WORKDIR/tpl" \
      GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=commit.gpgsign GIT_CONFIG_VALUE_0=false \
      LC_ALL=C PYTHONDONTWRITEBYTECODE=1 "$@"
}
command -v git sed python3
PYTHON_BIN="$(command -v python3)"
cd "$WORKDIR/clone"
d14env git switch -c add-quiet
d14env "$PYTHON_BIN" - <<'PY'
from pathlib import Path
p = Path('projects/text_tools/cli.py')
s = p.read_text()
old = '    parser.add_argument(\n        "--errors",'
assert s.count(old) == 1
s = s.replace(old, '''    parser.add_argument(
        "--quiet",
        action="store_true",
        help="Suppress the artifact-generation success summary.",
    )
    parser.add_argument(
        "--errors",''')
p.write_text(s)
PY
d14env git commit -am "add --quiet flag"
d14env "$PYTHON_BIN" projects/text_tools/cli.py --help | grep -F -- "--quiet"
mkdir -p "$WORKDIR/acts"
d14env printf 'a\nb\nc\n' > "$WORKDIR/acts/in.txt"
d14env "$PYTHON_BIN" projects/text_tools/cli.py --input "$WORKDIR/acts/in.txt" --split --output-dir "$WORKDIR/acts/out" --quiet
)
獨立掌握標準

封存的無協助評審寫明驗收標準(--quiet 下抑制摘要),引用自己執行中實際觀察到的 stdout 為證據,區分阻塞與非阻塞意見,區分假想修補程式與真實代理輸出、自己的檢查與隱藏評估器,且在預測與自己的檢查之後、協助對比之前已封存並提交。

來源資源與出處

linking CC BY-NC-SA 3.0 noncommercial share-alike

Pro Git, Second Edition

作者 / 機構: Scott Chacon and Ben Straub | 原始出處連結 ↗

對應章節 / 時間戳: Section 5.2 (Contributing to a Project: commit and review etiquette) and Section 6.2 (Contributing via pull requests)

驗證版本與日期: Second edition (Apress, 2014); verified 2026-10-30

入選理由:

Provides the shared vocabulary for PR-style review findings while the exercise stays entirely local and disposable.

來源前置條件:

Completion of day-14-act-03

安全回退方案(離線彈性):

The review rubric is fully specified in the lesson; no network access is required to complete it.

對比候選資源與選型分析 (2)

Industry-grade review guidance; offered as optional further reading since the exercise reviews a local disposable change.

Conventional Comments ↗ compared_alternative [CC BY 3.0]

Comment-labeling convention for review findings; optional vocabulary, not a substitute for the local rubric.