Skip to content

Commit 9ec2ada

Browse files
committed
重构最近更新生成逻辑并纳入 desktop.ini 删除
本次提交包含以下改动: 1. 重构 generate-last-updated-md.sh 的页面生成流程,统一为“固定文档头 + 本次记录块 + 历史记录块”结构。 2. 新增历史记录滚动保留机制,默认仅保留最近 3 条记录(可通过 --history-limit 调整),超过上限后按先进先出淘汰最旧记录。 3. 为每条记录引入 ENTRY_START/ENTRY_END 标记,保证脚本能够稳定提取并复用旧记录,避免原先硬编码拼接导致的定位错误。 4. 记录内保留 Summary、Index 与按文件展开的 diff 详情;空变更场景会生成可读的 No Changes 段落,不再出现结构断裂。 5. 锚点生成加入 entry_key 维度,避免多条历史记录之间的锚点冲突。 6. 参数能力扩展为 --staged / --range / --history-limit / --output,并补充严格的参数校验与函数级注释(含关键正则说明)。 7. 继续保持对 src/last-updated.md 与 src/sitemap.xml 的排除,防止 sitemap.xml 时间戳噪音持续污染最近更新;sitemap.txt 仍保留。 8. 纳入 desktop.ini 的删除变更,一并提交到版本历史。 改动目标: - 提升最近更新生成脚本在真实 Markdown/代码块内容下的稳定性与可维护性; - 让最近更新页面具备“可回看最近几次发布记录”的能力; - 控制页面体积增长,避免单次噪音变更长期放大。
1 parent 3ad4909 commit 9ec2ada

2 files changed

Lines changed: 168 additions & 90 deletions

File tree

desktop.ini

Lines changed: 0 additions & 4 deletions
This file was deleted.

generate-last-updated-md.sh

Lines changed: 168 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
# 该脚本是 “最近更新” 页面生成器,输出结构固定为:
77
# - H1 标题
88
# - Prenote 提示
9+
# - 最近 N 次变更记录(默认 3 次,先进先出)
10+
#
11+
# 每条“变更记录”包含:
12+
# - 记录标题(本次变更总标题)
913
# - Summary(生成时间、基准提交、diff 来源、统计)
1014
# - Index(文件索引 + +/-)
1115
# - 每个文件一个可折叠 diff 区块
@@ -21,6 +25,11 @@
2125
# - 默认排除 src/sitemap.xml(该文件时间戳噪音大,几乎每次都变)
2226
# - 保留 sitemap.txt(文章新增时具有信息价值)
2327
#
28+
# 历史保留策略:
29+
# - 默认最多保留 3 条记录(可通过 --history-limit 调整)
30+
# - 新记录始终在最上方
31+
# - 超出数量时淘汰最旧记录(FIFO)
32+
#
2433
# 依赖:
2534
# - git、bash、sed、awk、cksum、mktemp
2635
# ==============================================================================
@@ -46,6 +55,10 @@ FENCE="~~~~~"
4655

4756
MODE="staged"
4857
GIT_RANGE=""
58+
HISTORY_LIMIT=3
59+
60+
ENTRY_START_MARK="<!-- LAST_UPDATED_ENTRY_START -->"
61+
ENTRY_END_MARK="<!-- LAST_UPDATED_ENTRY_END -->"
4962

5063
# 默认排除 generated 文件自身和 sitemap.xml(仅时间戳噪音)
5164
EXCLUDE_FILES=(
@@ -55,19 +68,20 @@ EXCLUDE_FILES=(
5568

5669
usage() {
5770
# 打印脚本帮助信息。
58-
cat <<'EOF'
71+
cat <<'USAGE'
5972
Usage:
60-
./generate-last-updated-md.sh [--staged] [--range <git-range>] [--output <path>]
73+
./generate-last-updated-md.sh [--staged] [--range <git-range>] [--history-limit <n>] [--output <path>]
6174
6275
Options:
63-
--staged Use staged diff (default).
64-
--range <range> Use git range diff, e.g. abc123..def456.
65-
--output <path> Output markdown file path.
66-
-h, --help Show this help.
67-
EOF
76+
--staged Use staged diff (default).
77+
--range <range> Use git range diff, e.g. abc123..def456.
78+
--history-limit <n> Keep latest n records (FIFO), default: 3.
79+
--output <path> Output markdown file path.
80+
-h, --help Show this help.
81+
USAGE
6882
}
6983

70-
# 参数解析:将用户意图映射到 MODE + GIT_RANGE + OUTPUT_FILE。
84+
# 参数解析:将用户意图映射到 MODE + GIT_RANGE + HISTORY_LIMIT + OUTPUT_FILE。
7185
while [[ $# -gt 0 ]]; do
7286
case "$1" in
7387
--staged)
@@ -84,6 +98,14 @@ while [[ $# -gt 0 ]]; do
8498
GIT_RANGE="$2"
8599
shift 2
86100
;;
101+
--history-limit)
102+
if [[ $# -lt 2 ]]; then
103+
echo "Error: --history-limit requires a value." >&2
104+
exit 1
105+
fi
106+
HISTORY_LIMIT="$2"
107+
shift 2
108+
;;
87109
--output)
88110
if [[ $# -lt 2 ]]; then
89111
echo "Error: --output requires a value." >&2
@@ -109,6 +131,12 @@ if [[ "${MODE}" == "range" && -z "${GIT_RANGE}" ]]; then
109131
exit 1
110132
fi
111133

134+
# 正则 ^[1-9][0-9]*$:history limit 必须是正整数。
135+
if ! [[ "${HISTORY_LIMIT}" =~ ^[1-9][0-9]*$ ]]; then
136+
echo "Error: --history-limit must be a positive integer." >&2
137+
exit 1
138+
fi
139+
112140
# 防御式检查:防止在错误目录下执行并误写文件。
113141
if ! git -C "${PROOT}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
114142
echo "Error: ${PROOT} is not a git repository." >&2
@@ -122,9 +150,10 @@ fi
122150

123151
TMP_ROOT="${TMPDIR:-/tmp}"
124152
TMP_DIR="$(mktemp -d "${TMP_ROOT%/}/last-updated.XXXXXX")"
153+
125154
# 无论成功或失败都清理临时目录,避免残留 patch 文件。
126155
cleanup() {
127-
# 删除用于缓存每个文件 patch 的临时目录
156+
# 删除用于缓存每个文件 patch 与历史记录切片的临时目录
128157
rm -rf "${TMP_DIR}"
129158
}
130159
trap cleanup EXIT
@@ -147,8 +176,10 @@ make_anchor() {
147176
# 规则:
148177
# 1) 先把路径做 slug 化(仅保留字母数字,其他字符压成 -)
149178
# 2) 再拼接 cksum,避免不同路径 slug 冲突
179+
# 3) 再拼接 entry_key,避免不同“历史记录块”之间锚点冲突
150180
local file_path="$1"
151181
local index="$2"
182+
local entry_key="$3"
152183
local slug checksum
153184

154185
# sed 正则说明:
@@ -164,7 +195,7 @@ make_anchor() {
164195
slug="file-${index}"
165196
fi
166197

167-
printf 'f-%s-%s' "${slug}" "${checksum}"
198+
printf 'f-%s-%s-%s' "${entry_key}" "${slug}" "${checksum}"
168199
}
169200

170201
stat_to_display() {
@@ -205,89 +236,124 @@ collect_numstat() {
205236
printf '%s\t%s\n' "${added}" "${deleted}"
206237
}
207238

208-
write_header() {
209-
# 写入文档固定头部:标题、提示、Summary 统计。
210-
local generated_at="$1"
211-
local base_ref="$2"
212-
local diff_source="$3"
213-
local file_count="$4"
214-
local total_added="$5"
215-
local total_deleted="$6"
216-
local binary_count="$7"
217-
218-
cat > "${OUTPUT_FILE}" <<EOF
239+
write_document_header() {
240+
# 写入文档固定头部:H1 + Prenote。
241+
local target_file="$1"
242+
cat > "${target_file}" <<EOF_HEADER
219243
# ${TITLE}
220244
221245
## Prenote
222246
223247
> ${NOTICE_CONTENT}
224248
225-
## Summary
226-
227-
- Generated at: \`${generated_at}\`
228-
- Base commit: \`${base_ref}\`
229-
- Diff source: \`${diff_source}\`
230-
- Changed files: \`${file_count}\`
231-
- Total lines: \`+${total_added} / -${total_deleted}\`
232-
EOF
249+
EOF_HEADER
250+
}
233251

234-
if [[ "${binary_count}" -gt 0 ]]; then
235-
printf -- '- Binary-like diffs: `%s`\n' "${binary_count}" >> "${OUTPUT_FILE}"
236-
fi
252+
write_current_entry() {
253+
# 写入“本次变更记录块”,包含标题、Summary、Index 与 diff 详情。
254+
local target_file="$1"
255+
local entry_title="$2"
256+
local generated_at="$3"
257+
local base_ref="$4"
258+
local diff_source="$5"
259+
local file_count="$6"
260+
local total_added="$7"
261+
local total_deleted="$8"
262+
local binary_count="$9"
263+
264+
local i add_display del_display
265+
266+
{
267+
printf '%s\n' "${ENTRY_START_MARK}"
268+
printf '## %s\n\n' "${entry_title}"
269+
270+
printf '### Summary\n\n'
271+
printf -- '- Generated at: `%s`\n' "${generated_at}"
272+
printf -- '- Base commit: `%s`\n' "${base_ref}"
273+
printf -- '- Diff source: `%s`\n' "${diff_source}"
274+
printf -- '- Changed files: `%s`\n' "${file_count}"
275+
printf -- '- Total lines: `+%s / -%s`\n' "${total_added}" "${total_deleted}"
276+
if [[ "${binary_count}" -gt 0 ]]; then
277+
printf -- '- Binary-like diffs: `%s`\n' "${binary_count}"
278+
fi
279+
printf '\n'
280+
281+
if [[ "${file_count}" -eq 0 ]]; then
282+
printf '### No Changes\n\n'
283+
printf 'No file changes found for the selected diff source.\n\n'
284+
else
285+
printf '### Index\n\n'
286+
for ((i = 0; i < file_count; i++)); do
287+
printf '%s. [%s](#%s) `+%s / -%s`\n' \
288+
"$((i + 1))" \
289+
"${FILES[$i]}" \
290+
"${ANCHORS[$i]}" \
291+
"$(stat_to_display "${ADDED_STATS[$i]}")" \
292+
"$(stat_to_display "${DELETED_STATS[$i]}")"
293+
done
294+
printf '\n'
295+
296+
printf '### Diffs\n\n'
297+
for ((i = 0; i < file_count; i++)); do
298+
add_display="$(stat_to_display "${ADDED_STATS[$i]}")"
299+
del_display="$(stat_to_display "${DELETED_STATS[$i]}")"
300+
301+
printf '<a id="%s"></a>\n' "${ANCHORS[$i]}"
302+
printf '#### %s\n\n' "${FILES[$i]}"
303+
printf '<details>\n'
304+
printf '<summary><code>+%s / -%s</code> Click to expand diff</summary>\n\n' "${add_display}" "${del_display}"
305+
printf '%sdiff\n' "${FENCE}"
306+
cat "${DIFF_FILES[$i]}"
307+
printf '\n%s\n\n' "${FENCE}"
308+
printf '</details>\n\n'
309+
done
310+
fi
237311

238-
printf '\n' >> "${OUTPUT_FILE}"
312+
printf '%s\n\n' "${ENTRY_END_MARK}"
313+
} > "${target_file}"
239314
}
240315

241-
append_index() {
242-
# 写入 Index 目录,提供“文件 -> 锚点”跳转。
243-
local count="$1"
244-
local i
245-
printf '## Index\n\n' >> "${OUTPUT_FILE}"
246-
for ((i = 0; i < count; i++)); do
247-
printf '%s. [%s](#%s) `+%s / -%s`\n' \
248-
"$((i + 1))" \
249-
"${FILES[$i]}" \
250-
"${ANCHORS[$i]}" \
251-
"$(stat_to_display "${ADDED_STATS[$i]}")" \
252-
"$(stat_to_display "${DELETED_STATS[$i]}")" \
253-
>> "${OUTPUT_FILE}"
254-
done
255-
printf '\n' >> "${OUTPUT_FILE}"
256-
}
316+
extract_old_entries() {
317+
# 从旧版 last-updated.md 中提取历史记录块(按出现顺序:新 -> 旧)。
318+
# 仅识别被 ENTRY_START/ENTRY_END 包裹的块,旧格式(无标记)会被自动忽略。
319+
local source_file="$1"
320+
local line in_entry=0 entry_file="" idx=0
321+
322+
OLD_ENTRY_FILES=()
257323

258-
append_no_changes_note() {
259-
# 没有匹配到变更文件时写入兜底提示。
260-
cat >> "${OUTPUT_FILE}" <<'EOF'
261-
## No Changes
324+
if [[ ! -f "${source_file}" ]]; then
325+
return 0
326+
fi
262327

263-
No file changes found for the selected diff source.
328+
while IFS= read -r line || [[ -n "${line}" ]]; do
329+
if [[ "${line}" == "${ENTRY_START_MARK}" ]]; then
330+
in_entry=1
331+
entry_file="${TMP_DIR}/entry-old-${idx}.md"
332+
: > "${entry_file}"
333+
printf '%s\n' "${line}" >> "${entry_file}"
334+
continue
335+
fi
264336

265-
EOF
337+
if [[ "${in_entry}" -eq 1 ]]; then
338+
printf '%s\n' "${line}" >> "${entry_file}"
339+
if [[ "${line}" == "${ENTRY_END_MARK}" ]]; then
340+
OLD_ENTRY_FILES+=("${entry_file}")
341+
idx=$((idx + 1))
342+
in_entry=0
343+
fi
344+
fi
345+
done < "${source_file}"
266346
}
267347

268-
append_file_sections() {
269-
# 写入每个文件的正文区块:
270-
# - 文件标题
271-
# - 可折叠详情(details/summary)
272-
# - 原始 diff 内容
273-
local count="$1"
348+
append_old_entries() {
349+
# 将旧记录按顺序追加到目标文件,最多追加 keep_count 条。
350+
local target_file="$1"
351+
local keep_count="$2"
274352
local i
275-
local add_display del_display
276-
277-
for ((i = 0; i < count; i++)); do
278-
add_display="$(stat_to_display "${ADDED_STATS[$i]}")"
279-
del_display="$(stat_to_display "${DELETED_STATS[$i]}")"
280-
281-
{
282-
printf '<a id="%s"></a>\n' "${ANCHORS[$i]}"
283-
printf '## %s\n\n' "${FILES[$i]}"
284-
printf '<details>\n'
285-
printf '<summary><code>+%s / -%s</code> Click to expand diff</summary>\n\n' "${add_display}" "${del_display}"
286-
printf '%sdiff\n' "${FENCE}"
287-
cat "${DIFF_FILES[$i]}"
288-
printf '\n%s\n\n' "${FENCE}"
289-
printf '</details>\n\n'
290-
} >> "${OUTPUT_FILE}"
353+
354+
for ((i = 0; i < keep_count && i < ${#OLD_ENTRY_FILES[@]}; i++)); do
355+
cat "${OLD_ENTRY_FILES[$i]}" >> "${target_file}"
356+
printf '\n' >> "${target_file}"
291357
done
292358
}
293359

@@ -303,12 +369,15 @@ else
303369
fi
304370

305371
GENERATED_AT="$(date '+%Y-%m-%d %H:%M:%S %z')"
372+
ENTRY_KEY="$(printf '%s|%s|%s' "${GENERATED_AT}" "${BASE_REF}" "${DIFF_SOURCE}" | cksum | awk '{print $1}')"
373+
ENTRY_TITLE="更新记录(${GENERATED_AT} | ${BASE_REF}"
306374

307375
declare -a FILES
308376
declare -a ADDED_STATS
309377
declare -a DELETED_STATS
310378
declare -a ANCHORS
311379
declare -a DIFF_FILES
380+
declare -a OLD_ENTRY_FILES
312381

313382
TOTAL_ADDED=0
314383
TOTAL_DELETED=0
@@ -350,7 +419,7 @@ while IFS= read -r -d '' changed_file; do
350419
FILES+=("${changed_file}")
351420
ADDED_STATS+=("${added}")
352421
DELETED_STATS+=("${deleted}")
353-
ANCHORS+=("$(make_anchor "${changed_file}" "${FILE_COUNT}")")
422+
ANCHORS+=("$(make_anchor "${changed_file}" "${FILE_COUNT}" "${ENTRY_KEY}")")
354423
DIFF_FILES+=("${local_diff_file}")
355424

356425
file_has_binary_stat=false
@@ -376,13 +445,26 @@ while IFS= read -r -d '' changed_file; do
376445
FILE_COUNT=$((FILE_COUNT + 1))
377446
done < <("${FILE_SOURCE_CMD[@]}")
378447

379-
# 先写头部再写正文,No Changes 场景也能保留固定框架与统计信息。
380-
write_header "${GENERATED_AT}" "${BASE_REF}" "${DIFF_SOURCE}" "${FILE_COUNT}" "${TOTAL_ADDED}" "${TOTAL_DELETED}" "${BINARY_COUNT}"
448+
CURRENT_ENTRY_FILE="${TMP_DIR}/entry-current.md"
381449

382-
if [[ "${FILE_COUNT}" -eq 0 ]]; then
383-
append_no_changes_note
384-
exit 0
385-
fi
450+
# 先生成本次记录,再抽取旧记录,最后按“新 + 旧(最多 N-1 条)”重建文件。
451+
write_current_entry \
452+
"${CURRENT_ENTRY_FILE}" \
453+
"${ENTRY_TITLE}" \
454+
"${GENERATED_AT}" \
455+
"${BASE_REF}" \
456+
"${DIFF_SOURCE}" \
457+
"${FILE_COUNT}" \
458+
"${TOTAL_ADDED}" \
459+
"${TOTAL_DELETED}" \
460+
"${BINARY_COUNT}"
386461

387-
append_index "${FILE_COUNT}"
388-
append_file_sections "${FILE_COUNT}"
462+
extract_old_entries "${OUTPUT_FILE}"
463+
464+
write_document_header "${OUTPUT_FILE}"
465+
cat "${CURRENT_ENTRY_FILE}" >> "${OUTPUT_FILE}"
466+
printf '\n' >> "${OUTPUT_FILE}"
467+
468+
if (( HISTORY_LIMIT > 1 )); then
469+
append_old_entries "${OUTPUT_FILE}" "$((HISTORY_LIMIT - 1))"
470+
fi

0 commit comments

Comments
 (0)