feat(worklog-pending): 暫存改三段式的 merge、commit、abort
What:`tools/worklog-pending.sh` 新增 `merge {hash} {本次條目檔}`、`commit {hash} {併入清單}`、`abort {hash} {併入清單}` 三個子命令。`merge` 合成「暫存內容在前、本次條目在後」的檔案並印出 `MERGED=`、`CLAIM=`、`PENDING=`;`commit` 只刪併入清單上那幾個檔;`abort` 暫存一個都不刪,只丟掉合併檔與清單。原本的 `clear` 保留,改為清掉全部暫存。
Why:暫存只能在 wiki 寫入成功之後才清,但原本只有一個 `clear`,呼叫端要嘛先清再寫(寫失敗就兩邊都沒有),要嘛寫完清全部(把 `merge` 之後才存進來的內容一起刪掉)。兩種都會弄丟工作紀錄。
How:`merge` 只讀不刪,刪檔一律等 `commit`,也就是等 wiki 回報成功。只刪清單上的檔案,是為了保住 `merge` 之後、`commit` 之前另一個工作階段新存進來的內容。`commit` 只認 `merge` 產出的清單,而且清單上的路徑必須落在該 hash 的暫存目錄底下,不然當用法錯誤擋下來——這道護欄擋的是拿別處的清單來刪檔。`merge` 在沒有暫存時仍然回 `0`,合併檔至少有本次條目,呼叫端不必分兩條路走。
Who:`jsc-log:worklog` 寫日誌那一步,以及用 `stage-report.sh --pending-file` 暫存過內容的 SDLC 階段。
This commit is contained in:
@@ -6,24 +6,39 @@
|
||||
# 階段回報發現沒有日誌,就把該階段的內容存進本區並警告使用者;下次 jsc-log:worklog 寫入時
|
||||
# 先把本區的內容一併寫進去,寫完才清掉。
|
||||
#
|
||||
# 一次寫入的三段式(merge、commit、abort)解決同一件事:暫存只能在 wiki 寫入成功之後才清。
|
||||
# merge 把暫存內容與本次條目合成一份要寫進 wiki 的檔案,同時記下這次併走了哪些暫存檔(併入清單)。
|
||||
# commit wiki 寫入成功後呼叫,只刪掉併入清單上那幾個檔。
|
||||
# abort wiki 寫入失敗後呼叫,暫存原封不動,只丟掉合併檔與併入清單。
|
||||
# 只刪清單上的檔案,是為了保住 merge 之後、commit 之前另一個工作階段新存進來的內容。
|
||||
#
|
||||
# 用法:
|
||||
# worklog-pending.sh add <hash> <file> 把一段待寫入的日誌內容存起來,印出存放路徑
|
||||
# worklog-pending.sh list <hash> 列出該 hash 的暫存檔路徑(一行一個,依時間排序)
|
||||
# worklog-pending.sh cat <hash> 依時間順序印出全部暫存內容
|
||||
# worklog-pending.sh clear <hash> 清掉該 hash 的暫存(寫入日誌成功後才做)
|
||||
# worklog-pending.sh clear <hash> 清掉該 hash 的全部暫存(寫入日誌成功後才做)
|
||||
# worklog-pending.sh merge <hash> <file> 合成「暫存內容+本次條目」,印出 MERGED= 與 CLAIM=
|
||||
# worklog-pending.sh commit <hash> <claim> 寫入成功後清掉併入清單上的暫存檔
|
||||
# worklog-pending.sh abort <hash> <claim> 寫入失敗後保留暫存,只丟掉合併檔與併入清單
|
||||
#
|
||||
# <hash> 為 jsc-gitea/tools/hash-id 算出的 8 碼工作日誌 hash,也就是 LOG_{HASH} 的 HASH。
|
||||
# 存放位置: $JSC_HOME/worklog-pending/{hash}/{UTC 時間}-{pid}.md(JSC_HOME 預設 ~/.jsc)
|
||||
# 合併檔: $JSC_HOME/worklog-pending/.merge/{hash}-{UTC 時間}-{pid}.md
|
||||
# 併入清單: 同名換副檔名 .claim,一行一個被併走的暫存檔路徑
|
||||
#
|
||||
# 結束碼: 0=成功 1=讀寫失敗 2=用法錯誤 3=該 hash 沒有暫存內容
|
||||
# merge 沒有暫存內容時仍然 exit 0:合併檔至少有本次條目,呼叫端不必分兩條路走。
|
||||
#
|
||||
# 陷阱:
|
||||
# - clear 只在日誌確實寫進 wiki 之後才呼叫。先清再寫,寫失敗就兩邊都沒有。
|
||||
# - clear 與 commit 都只在日誌確實寫進 wiki 之後才呼叫。先清再寫,寫失敗就兩邊都沒有。
|
||||
# - commit 只認 merge 產出的併入清單,且清單上的路徑必須落在該 hash 的暫存目錄底下,
|
||||
# 不然就當成用法錯誤擋下來——這道護欄擋的是拿別人的清單來刪檔。
|
||||
# - 檔名帶 UTC 時間與 pid,同一秒內兩個工作階段各存各的,不會互相覆蓋。
|
||||
set -eu
|
||||
|
||||
JSC_HOME="${JSC_HOME:-$HOME/.jsc}"
|
||||
ROOT="$JSC_HOME/worklog-pending"
|
||||
MERGE_DIR="$ROOT/.merge"
|
||||
|
||||
usage() {
|
||||
cat >&2 <<'EOF'
|
||||
@@ -31,7 +46,10 @@ usage() {
|
||||
worklog-pending.sh add <hash> <file> 存一段待寫入的日誌內容
|
||||
worklog-pending.sh list <hash> 列出暫存檔路徑
|
||||
worklog-pending.sh cat <hash> 印出全部暫存內容
|
||||
worklog-pending.sh clear <hash> 清掉暫存(寫入日誌成功後才做)
|
||||
worklog-pending.sh clear <hash> 清掉全部暫存(寫入日誌成功後才做)
|
||||
worklog-pending.sh merge <hash> <file> 合成「暫存內容+本次條目」,印出 MERGED= 與 CLAIM=
|
||||
worklog-pending.sh commit <hash> <claim> 寫入成功後清掉併入清單上的暫存檔
|
||||
worklog-pending.sh abort <hash> <claim> 寫入失敗後保留暫存,只丟掉合併檔與併入清單
|
||||
結束碼: 0=成功 1=讀寫失敗 2=用法錯誤 3=沒有暫存內容
|
||||
EOF
|
||||
exit 2
|
||||
@@ -44,6 +62,14 @@ valid_hash() { # 只收 8 碼大寫英數,擋掉路徑穿越
|
||||
esac
|
||||
}
|
||||
|
||||
valid_claim() { # 併入清單只認 merge 產出的那一份,擋掉拿別處的清單來刪檔
|
||||
case "$1" in
|
||||
"$MERGE_DIR"/*.claim) ;;
|
||||
*) echo "[jsc][工作日誌暫存][ERR]:併入清單須是 merge 產出的「$MERGE_DIR/*.claim」,收到「$1」。" >&2; exit 2 ;;
|
||||
esac
|
||||
[ -f "$1" ] || { echo "[jsc][工作日誌暫存][ERR]:找不到併入清單「$1」。" >&2; exit 1; }
|
||||
}
|
||||
|
||||
cmd="${1:-}"; [ -n "$cmd" ] || usage
|
||||
shift || true
|
||||
hash="${1:-}"; [ -n "$hash" ] || usage
|
||||
@@ -87,6 +113,70 @@ case "$cmd" in
|
||||
rm -rf "$dir" || { echo "[jsc][工作日誌暫存][ERR]:清不掉「$dir」。" >&2; exit 1; }
|
||||
printf '已清除暫存:%s\n' "$dir"
|
||||
;;
|
||||
merge)
|
||||
# 合成這一次要寫進 wiki 的內容:暫存的在前(依時間),本次條目在後。
|
||||
# 這裡只讀不刪——刪檔一律等 commit,也就是等 wiki 寫入回報成功。
|
||||
src="${2:-}"
|
||||
[ -n "$src" ] || usage
|
||||
[ -f "$src" ] || { echo "[jsc][工作日誌暫存][ERR]:找不到本次條目檔「$src」。" >&2; exit 1; }
|
||||
mkdir -p "$MERGE_DIR" || { echo "[jsc][工作日誌暫存][ERR]:建不出合併目錄「$MERGE_DIR」。" >&2; exit 1; }
|
||||
stamp=$(date -u +%Y%m%dT%H%M%SZ)
|
||||
merged="$MERGE_DIR/$hash-$stamp-$$.md"
|
||||
claim="$MERGE_DIR/$hash-$stamp-$$.claim"
|
||||
: > "$merged" || { echo "[jsc][工作日誌暫存][ERR]:寫不進「$merged」。" >&2; exit 1; }
|
||||
: > "$claim" || { echo "[jsc][工作日誌暫存][ERR]:寫不進「$claim」。" >&2; exit 1; }
|
||||
taken=0
|
||||
if [ -d "$dir" ]; then
|
||||
for f in "$dir"/*.md; do
|
||||
[ -f "$f" ] || continue
|
||||
cat "$f" >> "$merged" || { echo "[jsc][工作日誌暫存][ERR]:讀不到「$f」。" >&2; exit 1; }
|
||||
printf '\n' >> "$merged"
|
||||
printf '%s\n' "$f" >> "$claim"
|
||||
taken=$((taken + 1))
|
||||
done
|
||||
fi
|
||||
cat "$src" >> "$merged" || { echo "[jsc][工作日誌暫存][ERR]:併不進本次條目「$src」。" >&2; exit 1; }
|
||||
printf '\n' >> "$merged"
|
||||
printf 'MERGED=%s\n' "$merged"
|
||||
printf 'CLAIM=%s\n' "$claim"
|
||||
printf 'PENDING=%s\n' "$taken"
|
||||
;;
|
||||
commit)
|
||||
# 護欄:只有 wiki 寫入成功才輪到這一段。清單以外的暫存檔一律不動,
|
||||
# 因為那是 merge 之後才存進來的內容,還沒被寫進任何一頁。
|
||||
claim="${2:-}"
|
||||
[ -n "$claim" ] || usage
|
||||
valid_claim "$claim"
|
||||
cleared=0
|
||||
while IFS= read -r f; do
|
||||
[ -n "$f" ] || continue
|
||||
case "$f" in
|
||||
"$dir"/*) ;;
|
||||
*) echo "[jsc][工作日誌暫存][ERR]:併入清單裡的「$f」不在「$dir」底下,不清。" >&2; exit 2 ;;
|
||||
esac
|
||||
[ -e "$f" ] || continue
|
||||
rm -f "$f" || { echo "[jsc][工作日誌暫存][ERR]:清不掉「$f」。" >&2; exit 1; }
|
||||
cleared=$((cleared + 1))
|
||||
done < "$claim"
|
||||
rmdir "$dir" 2>/dev/null || true
|
||||
rm -f "$claim" "${claim%.claim}.md"
|
||||
printf '已清除暫存 %s 個檔案(wiki 寫入成功後才做)。\n' "$cleared"
|
||||
;;
|
||||
abort)
|
||||
# wiki 寫入失敗時走這裡:暫存一個都不刪,下一次 merge 會再把它們併進去。
|
||||
claim="${2:-}"
|
||||
[ -n "$claim" ] || usage
|
||||
valid_claim "$claim"
|
||||
kept=0
|
||||
while IFS= read -r f; do
|
||||
[ -n "$f" ] || continue
|
||||
if [ -e "$f" ]; then
|
||||
kept=$((kept + 1))
|
||||
fi
|
||||
done < "$claim"
|
||||
rm -f "$claim" "${claim%.claim}.md"
|
||||
printf '暫存保留 %s 個檔案,合併檔與併入清單已丟掉。\n' "$kept"
|
||||
;;
|
||||
*)
|
||||
usage ;;
|
||||
esac
|
||||
|
||||
Reference in New Issue
Block a user