前言

高考结束后的那个夏天,我没有选择狂欢,而是一头扎进了代码的世界。当两个月的算法与数据结构探索之旅告一段落,我决心将这段经历记录下来,让努力留下可视化的痕迹。为了实现这个目标,我将目光投向了 GitHub ,我计划用 GitHub 为自己打造一个独一无二的、能够动态展示我的学习历程的个人主页。

我了解到,常规的 git push 会将所有提交的时间戳记为当前时刻,导致 GitHub 贡献图无法真实反映、精确还原我暑假期间的学习轨迹。

为了解决这一问题,并为未来的学习过程建立一个可追溯、可视化的档案,我设计并实施了本次手动迁移与版本历史构建的任务。

这篇文章,将详细记录我从连 Git 指令都敲不对,到最终利用 Python 脚本实现自动化操作的完整过程以及心路历程。


手动实现:基础工作流与问题排查

我决定采用 git commit 命令,为每一份题解代码创建带有历史时间戳的提交。这个方案虽然可行,但在手动执行的过程中,我遇到并解决了一系列典型的命令行与 Git 环境配置问题。下面将对这些问题进行复盘。

核心实现思路

本次操作的技术基石是 Git 的 commit 命令所提供的 --date 参数。该参数允许用户在创建提交时,指定一个自定义的时间戳,从而实现对版本历史的回溯性构建。

我设计的基础工作流的操作流程如下:

  1. 将代码文件添加至工作区。

  2. 在代码文件的前四行手动添加格式规范的注释,格式如下:

    // Problem: 练习平台 题号 题目
    // Link: 题目链接
    // Author: nine19een
    // Date: 日期
    //
    // 代码正文

  3. 使用 git add 将文件变更添加至暂存区。

  4. 执行带有特定历史日期的 git commit 命令以创建提交。

核心命令示例:

git commit --date="YYYY-MM-DD HH:MM:SS" -m "Creat 文件名"

其中,--date 参数用于指定历史时间戳,可通过练习平台的提交记录获取并手动替换进指令里。

环境配置与故障排查

在将上述流程的实践过程中,我遇到并解决了以下几个典型的环境配置与命令行操作问题。

Case 1: 路径导航错误 - No such file or directory

  • 现象:
    在 Git Bash 环境下,使用 cd 命令并提供 Windows 文件管理器中的标准路径,无法成功切换目录,返回 No such file or directory 错误。

  • 问题分析:
    该错误源于 Windows 与类 Unix 环境 (Git Bash) 在路径表示法上的差异。主要有两点:

    1. 路径分隔符: Windows 使用 \,而 Git Bash 需要 /,因此,直接在Windows的文件资源管理器中复制路径并粘贴到 Git Bash 窗口是不可行的!
    2. 路径完整性: 必须提供从盘符开始的完整、正确的路径结构。
  • 解决方案:

    1. 格式修正: 若选择路径复制/粘贴方案,需手动将路径中的 \ 全部替换为 /,并确保路径完整(如 C:/Users/YourName/Documents/...)。

    2. GUI 辅助: 作为一种更高效且不易出错的方法,可以在 cd 命令后,直接将目标文件夹从文件管理器拖拽至 Git Bash 窗口,以自动生成符合其语法规范的完整路径。强烈建议使用该方法,不易出错

      将文件拖拽进 Git Bash 窗口即会自动生成完整路径。

      再按回车即可。

Case 2: 仓库定位错误 - fatal: not a git repository

  • 现象:
    git clone 操作成功后,在当前目录(即 clone 命令的执行目录)下运行 git statusgit add 等命令,系统返回致命错误,提示当前目录并非一个 Git 仓库。

  • 问题分析:
    git clone 命令会在当前目录下创建一个新的子目录作为仓库的根目录。所有 Git 相关的操作,都必须在这个新生成的子目录(或其内部)执行,因为根目录中包含了必需的 .git 元数据文件夹。错误发生时,我正处在仓库的父目录。

  • 解决方案:
    执行 git clone 后,必须使用 cd <repository-name> 命令,进入到新克隆的仓库根目录,再执行后续的 Git 操作。

Case 3: 网络连接失败 - schannel: failed to receive handshake, SSL/TLS connection failed

  • 现象:
    在解决了本地的路径和仓库定位问题后,我遇到了最棘手的一个 blocker。当执行 git push, git pull, git fetch 等任何需要与远程服务器通信的网络操作时,命令会在长时间等待后失败,并返回一个底层网络错误:schannel: failed to receive handshake, SSL/TLS connection failed

    最令人困惑的是,与此同时,我的浏览器却可以正常、快速地访问 github.com 网站。

  • 问题分析:
    这个现象——“浏览器可以,但命令行不行”——是一个非常典型的网络代理配置问题

    问题根源在于,我的电脑上运行了 Clash 这样的网络代理工具来优化网络访问。浏览器被正确配置为通过代理来访问 GitHub,所以连接顺畅。然而,Git Bash 作为一个独立的命令行环境,默认不会自动使用系统的代理设置。

    因此,Git 仍在尝试直接连接 GitHub 的服务器,这条直连路径受到了网络环境的干扰,导致在 SSL/TLS 加密握手阶段就因超时而失败。schannel 是 Windows 系统用于处理此过程的内置安全库的名称。

  • 解决方案:
    解决方案的核心是:必须为 Git 命令行工具,手动配置与系统代理一致的代理服务器。

    这个过程分为两步:

    1. 定位代理端口号:
      首先,需要找到 Clash 代理软件为 HTTP/HTTPS 协议监听的本地端口号。这里我以我使用的 Clash Verge 进行举例。

    2. 为 Git 配置全局代理:
      接着,需要在 Git Bash 内执行以下两条命令,将 Git 的 http.proxyhttps.proxy 都指向本地代理的监听地址 (http://127.0.0.1:端口号)。P.S. 127.0.0.1 是一个永远指向本机的特殊 IP 地址。

      • 将 Git 的 HTTP 流量指向本地端口:

        git config --global http.proxy http://127.0.0.1:端口号

      • 将 Git 的 HTTPS 流量也指向本地端口 (关键):

        git config --global https.proxy http://127.0.0.1:端口号

在执行完这两条命令,成功地为 Git “开启代理”之后,再次运行 git fetch,网络连接问题迎刃而解。

Case 4: 推送被拒绝 - ! [rejected] main -> main (fetch first)

  • 现象:
    在本地进行 commit 操作后,执行 git push 时,推送被远程服务器拒绝。提示信息指出,远程仓库包含了本地所没有的修改。

  • 问题分析:
    这是 Git 的一种保护机制,旨在防止本地的推送意外覆盖掉远程仓库上其他人(或自己在别处)的提交。这种情况通常发生于:在本地 clone 或上次 pull 之后,远程仓库又有了新的 commit(例如,直接在 GitHub 网站上修改了 README.md 文件)。

  • 解决方案:
    请务必在每次 git push 之前先执行 git pull ,以确保本地和远程仓库的文件更新保持同步。

    1. 执行 git pull 命令,将远程的最新变更下载到本地并自动合并。
    2. 在解决了可能出现的合并冲突(对于个人项目,通常会自动合并成功)后,再次执行 git push

Case 5: 换行符警告 - LF will be replaced by CRLF

  • 现象:
    git add 一个文件时,Git bash 会出现一条警告,提示文件中的 LF 将会被 CRLF 替换。

  • 问题分析:
    这是由于不同操作系统使用的换行符标准不同导致的。Unix/Linux/macOS 使用 LF ,而 Windows 使用 CRLF 。Git 内置了 core.autocrlf 功能,能够自动在不同系统间转换换行符,以保证版本库中的换行符格式统一。这个警告正是该功能在工作的体现。

  • 解决方案:
    无视风险,继续访问()。无需任何操作,可以安全地忽略此警告。因为这是 Git 的正常行为,代表它正在后台为我们处理跨平台兼容性问题。

Case 6: 暂存区管理 - 在误操作后如何撤销 git add

  • 现象:
    在执行 git add . 后,发现将一些不需要的文件(或错误的修改)添加到了暂存区,需要在 commit 前进行撤销。

  • 问题分析:
    这是 Git 工作流中非常常见的需求。需要一个命令,能将文件从暂存区安全地移回工作区,而不丢失任何代码修改。

  • 解决方案:

    1. 撤销所有暂存: 使用 git reset 命令,可以一次性清空整个暂存区。

      git reset

    2. 撤销单个文件: 使用 git restore --staged <file> 命令,可以精确地将指定文件移出暂存区。

      git restore --staged <filename>

    这两种方式都不会修改工作区的文件内容,非常安全。


自动化实现:从“体力劳动”到“系统构建”

历时四个小时的手动历史提交迁移结束后,我立刻意识到:为这上百份题解代码,手动在 README.md 中创建并维护一个格式统一、内容准确、且按日期排序的表格,不仅极度耗时,且极易出错。我会死掉的

因此,我决定采用 Python 编写自动化脚本,将整个 README.md 的更新流程自动化。P.S.懒惰是人类的第一生产力。

核心思路:两阶段任务分解

为了降低复杂度并确保每一步都稳定可靠,我将整个自动化任务分解为两个独立的、前后关联的子项目:

  1. 项目一:源代码注释规范化

    • 目标: 遍历所有源代码文件,对所有注释不规范的代码进行更新,注释格式详见手动实现:基础工作流与问题排查 - 核心实现思路 - 2.

    • 作用: 为后续的 README 生成器提供一个统一、可靠、可离线解析的数据源。

  2. 项目二:README 生成器

    • 目标: 读取所有经过规范化的源代码文件,提取其头部注释中的元数据,并生成最终的、完整的 Markdown 表格,用于在 README.md 中生成一个包含所有题目/题解信息的表格。

    • 作用: 彻底替代手动编辑 README 的工作。

项目一:源代码注释规范化脚本

Case 1: 编码格式不统一 - 中文乱码

  • 现象:
    当我在脚本尝试读取 .cpp 文件内容时,或在使用 CLion 打开从 GitHub 克隆的文件时,文件中的中文注释显示为乱码。

  • 问题分析:
    这是典型的字符编码不匹配问题。GitHub 和 CLion 默认使用 UTF-8 编码,它兼容全球所有语言。而我之前做题时使用的 Dev-C++ 在 Windows环境下,默认使用本地化的 GBK 编码保存文件。当一个程序尝试用 UTF-8 编码去读取一个 GBK 编码的文件时,就会产生乱码。

  • 解决方案:
    在整个开发链条中,强制统一使用 UTF-8 编码。

    1. 开发环境迁移: 彻底弃用对 UTF-8 支持不佳的旧 IDE,将主力开发环境迁移至现代化、默认使用 UTF-8 的 CLion。
    2. 脚本读写配置: 在 Python 脚本中,使用 open() 函数时,明确指定 encoding 参数。读取时使用 encoding='utf-8-sig' 以兼容 Windows 下带 BOM 的 UTF-8 文件,写入时使用 encoding='utf-8'
      1
      2
      3
      4
      5
      6
      7
      # 读取时指定编码
      with open(file_path, 'r', encoding='utf-8-sig') as f:
      lines = f.readlines()

      # 写入时同样指定编码
      with open(file_path, 'w', encoding='utf-8') as f:
      f.writelines(lines)

最终脚本:update_sources.py

该脚本的核心逻辑是:遍历指定目录下的所有 C++ 文件,检查其是否已包含标准注释头。如果没有,则从文件名中解析题号,通过网络请求访问题目 URL 抓取完整标题,并从 Git 历史中获取文件首次提交的日期。最后,将这些元数据整合成标准格式的注释块,并重写回文件头部。

在让Gemini充分了解了我的诉求后,一份自动化更新注释的 Python 脚本便诞生了。

点击展开/折叠 `update_sources.py` 完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import os
import requests
from bs4 import BeautifulSoup
import re
import time

REPO_FOLDER = "Coding-Practice"

def get_luogu_title(url):
try:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()

soup = BeautifulSoup(response.text, 'lxml')

title_tag = soup.find('h1')
if title_tag:
return title_tag.get_text(strip=True)
else:
return None
except requests.exceptions.RequestException as e:
print(f" - 网络错误: 无法访问 {url} - {e}")
return None

def process_file(file_path):
try:
with open(file_path, 'r', encoding='utf-8-sig') as f:
lines = f.readlines()

if not lines:
print(f" - 跳过 (文件为空): {os.path.basename(file_path)}")
return

first_line_content = lines[0].strip()
if first_line_content == "// Problem: Luogu":
print(f" - 处理中: {os.path.basename(file_path)}")

link_url = ""
for line in lines:
if "// Link:" in line:
match = re.search(r'https?://[^\s]+', line)
if match:
link_url = match.group(0)
break

if not link_url:
print(f" - 错误: 在文件中未找到有效的 Link URL。")
return

title = get_luogu_title(link_url)
time.sleep(0.5)

if title:
lines[0] = f"// Problem: Luogu {title}\n"
with open(file_path, 'w', encoding='utf-8') as f:
f.writelines(lines)
print(f" - 更新成功!标题已设置为: {title}")
else:
print(f" - 更新失败: 未能从 {link_url} 获取标题。")
else:
print(f" - 跳过 (已处理): {os.path.basename(file_path)}")

except Exception as e:
print(f" - 处理文件时发生未知错误: {os.path.basename(file_path)} - {e}")

def main():
if not os.path.isdir(REPO_FOLDER):
print(f"错误: 文件夹 '{REPO_FOLDER}' 不存在。请确保脚本和该文件夹在同一目录下。")
return

print(f"--- 开始扫描文件夹: {REPO_FOLDER} ---")

all_files = sorted([f for f in os.listdir(REPO_FOLDER) if f.startswith("Luogu") and f.endswith(".cpp")])

for filename in all_files:
full_path = os.path.join(REPO_FOLDER, filename)
process_file(full_path)

print("\n--- 扫描完成 ---")

if __name__ == "__main__":
main()

项目二:README 生成器脚本

在所有源文件都拥有了标准化的元数据之后,生成 README 的任务就变得纯粹而直接。

Case 1: 爬虫失效

  • 现象:
    update_sources.py 的开发过程中,用于抓取洛谷题目难度的爬虫函数,在多次尝试后依然反复失效,返回 N/A 或空的页面内容,即便 URL 在浏览器中可以正常访问。

  • 问题分析:
    我编写了一个独立的脚本,将 requests 库获取到的原始 HTML 内容保存下来后,发现我收到的并非洛谷的题目页面,而是 Cloudflare 的人机验证页面。大概是我的操作被 Cloudflare 的反爬虫机制识别并拦截了。

  • 解决方案:

    1. 方案迭代: 我先后尝试了多种爬虫策略,包括更换 User-Agent、寻找特定的 HTML 标签(洛谷难度标签为<span>)、甚至用正则表达式直接匹配页面数据脚本,但都因为反爬虫机制的存在而宣告失败。
    2. 最终方案: 我突然意识到,这些题目与其对应的难度,我可以直接从洛谷个人主页里保存到本地,将其变成静态数据。

    因此,我做出了一个关键的架构决策:放弃动态爬取,转向静态的本地数据源。

    我将所有题目的难度信息,手动整理成一个 Python 字典,直接内置在脚本中。

    1
    2
    3
    4
    5
    6
    # 本地难度数据库示例
    DIFFICULTY_DATA = {
    "P1001": "入门",
    "P1002": "普及−",
    # ... and so on
    }

    这个决策,让脚本彻底摆脱了 Cloudflare 的困扰,100% 可靠且运行速度速度极佳,也易于长期维护。

Case 2: 逻辑疏漏

  • 现象:
    脚本初版成功生成了所有新题目的表格行,但在后续迭代中,我发现它会丢失 README.md 中已有的、非洛谷平台的记录(如 Codeforces)。

  • 问题分析:
    我的脚本只考虑了“生成新的”,而没有考虑“保留旧的”和“合并排序”。

  • 解决方案:
    重构脚本的核心逻辑,使其具备多平台兼容性:

    1. 全面读取: 读取旧 README 时,不再只筛选洛谷题目,而是将所有表格行都加载到内存中。
    2. 增量处理: 只为那些文件名不存在于旧记录中的新文件,生成新的表格行。
    3. 合并排序: 将新生成的行与所有旧的行合并成一个总列表,最后对这个总列表,进行统一的日期降序排序。

最终脚本:generate_readme.py

这是经历了多次重构和 BUG 修复后的最终版本。它整合了本地难度数据库、多平台兼容、增量更新和全量排序等所有功能。

脚本运行后,会生成一个 update.txt 文件,包含了所有新旧题目的、完美排序的最终表格,我只需要将其整体复制并替换 README.md 中的旧表格即可。

点击展开/折叠 `update_sources.py` 完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
import os
import re
from datetime import datetime
from urllib.parse import unquote

# --- Configuration ---
GITHUB_USERNAME = "nine19een"
REPO_NAME = "Coding-Practice"
SOURCE_CODE_PATH = "Coding-Practice"
README_FILE_PATH = "README.md"
OUTPUT_FILENAME = "update.txt"
# ---------------------

# ==============================================================================
# Local Difficulty Database
# ==============================================================================
DIFFICULTY_DATA = {
"P1001": "入门", "P1046": "入门", "P1047": "入门", "P1085": "入门",
# 略
}
# ==============================================================================

BADGE_TEMPLATES = {
"入门": '<img src="https://img.shields.io/badge/入门-FE4C61?style=for-the-badge&textColor=white" alt="入门">',
"普及−": '<img src="https://img.shields.io/badge/普及−-F39C11?style=for-the-badge&textColor=white" alt="普及−">',
# 略
}

def load_existing_entries(readme_path):
existing_entries = []
in_table_section = False
if not os.path.exists(readme_path):
return existing_entries
try:
with open(readme_path, 'r', encoding='utf-8') as f:
for line in f:
if line.strip().startswith('| :---'):
in_table_section = True
continue
if in_table_section and (not line.strip().startswith('|') or not line.strip()):
in_table_section = False

if in_table_section:
date_match = re.search(r'\|\s*(\d{4}-\d{2}-\d{2})', line)
date_str = date_match.group(1) if date_match else "1970-01-01"
existing_entries.append({
"date_obj": datetime.strptime(date_str, '%Y-%m-%d'),
"row": line.strip()
})
except Exception as e:
print(f"Warning: Failed to read README file: {e}")
return existing_entries

def parse_source_file_metadata(file_path):
metadata = {'platform': 'unknown'}
try:
with open(file_path, 'r', encoding='utf-8-sig') as f:
content = f.read()

date_match = re.search(r'//\s*Date:\s*(\d{4}-\d{2}-\d{2})', content)
if date_match: metadata['date'] = date_match.group(1)

link_match = re.search(r'//\s*Link:\s*(https?://[^\s]+)', content)
if link_match: metadata['link'] = link_match.group(1)

problem_match = re.search(r'//\s*Problem:\s*(.*)', content)
if problem_match:
line_content = problem_match.group(1).strip()
if line_content.lower().startswith("luogu"):
metadata['platform'] = 'luogu'
metadata['details'] = line_content[5:].strip()
elif line_content.lower().startswith("codeforces"):
metadata['platform'] = 'codeforces'
metadata['details'] = line_content[10:].strip()
elif line_content.lower().startswith("opj"):
metadata['platform'] = 'opj'
metadata['details'] = line_content[3:].strip()
return metadata
except Exception as e:
print(f" - Error: Failed to parse file {os.path.basename(file_path)}: {e}")
return None

def build_markdown_row(metadata, filename):
date = metadata.get('date', 'N/A')
platform = metadata.get('platform')
details = metadata.get('details', '')
link = metadata.get('link', '#')

problem_md, difficulty_badge = f"[{platform}-{details}]({link})", ""

if platform == 'luogu':
pid_match = re.search(r'([PB]\d+)', details)
if pid_match:
problem_id = pid_match.group(1)
title = re.sub(r'「.*?」|\[.*?\]|【.*?】', '', details).replace(problem_id, '').strip()
problem_md = f"[{'洛谷'}-{problem_id}-{title}]({link})"
difficulty_text = DIFFICULTY_DATA.get(problem_id, "未定义")
difficulty_badge = BADGE_TEMPLATES.get(difficulty_text, BADGE_TEMPLATES["未定义"])
elif platform == 'codeforces':
id_letter_match = re.search(r'-([A-F]\d?)-', filename) or re.search(r'\s+([A-F]\d?)\.', details)
if id_letter_match:
id_letter = id_letter_match.group(1)[0]
badge_key = f"Div.4 {id_letter}"
difficulty_badge = BADGE_TEMPLATES.get(badge_key, "**CF**")
else:
difficulty_badge = "**CF**"
problem_md = f"[Codeforces-{details}]({link})"
elif platform == 'opj':
pid_match = re.search(r'(\d+)', details)
problem_id = pid_match.group(1) if pid_match else ''
title = details.replace(problem_id, '').strip()
problem_md = f"[opj-{problem_id}-{title}]({link})"
difficulty_badge = BADGE_TEMPLATES.get("基础", "")

safe_filename = filename.replace('+', '%2B')
solution_md = f"[View Code (C++)](https://github.com/{GITHUB_USERNAME}/{REPO_NAME}/blob/main/{safe_filename})"

return {
"date_obj": datetime.strptime(date, '%Y-%m-%d'),
"row": f"| {date} | {problem_md} | {difficulty_badge} | {solution_md} |"
}

def main():
print("--- Initializing README Generation ---")

existing_entries = load_existing_entries(README_FILE_PATH)
existing_filenames = set(unquote(re.search(r'/main/(.*)\)', entry['row']).group(1)) for entry in existing_entries if re.search(r'/main/(.*)\)', entry['row']))

print(f"Found {len(existing_entries)} existing entries in README.")

all_entries = existing_entries

if not os.path.isdir(SOURCE_CODE_PATH):
print(f"Error: Source code directory '{SOURCE_CODE_PATH}' not found.")
return

print(f"\n--- Scanning for new source files in {SOURCE_CODE_PATH} ---")

all_cpp_files = sorted([f for f in os.listdir(SOURCE_CODE_PATH) if f.endswith(".cpp")])
new_files_processed = 0

for filename in all_cpp_files:
if filename in existing_filenames:
continue

print(f" - Processing new file: {filename}")
new_files_processed += 1

file_path = os.path.join(SOURCE_CODE_PATH, filename)
metadata = parse_source_file_metadata(file_path)

if not metadata or 'date' not in metadata:
print(f" - Warning: Could not parse required metadata from {filename}. Skipping.")
continue

new_entry = build_markdown_row(metadata, filename)
all_entries.append(new_entry)

if new_files_processed == 0:
print("\n--- No new files to add. README is up to date. ---")
return

print(f"\n--- Processed {new_files_processed} new files. Generating final table. ---")

all_entries.sort(key=lambda x: x['date_obj'], reverse=True)

try:
with open(OUTPUT_FILENAME, 'w', encoding='utf-8') as f:
f.write("| Date | Problem | Difficulty | Solution |\n")
f.write("| :--------- | :--------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------- |\n")
for entry in all_entries:
f.write(entry['row'] + '\n')

print("\n" + "="*50)
print(f"Success! The complete and sorted table has been saved to:")
print(f"{os.path.abspath(OUTPUT_FILENAME)}")
print("Please copy the entire content of this file and replace the old table in your README.")
print("="*50)
except Exception as e:
print(f"\nError! Failed to write output file: {e}")

if __name__ == "__main__":
main()

最终成果展示

经过上述一系列的手动迁移、自动化脚本构建与 README.md 内容生成,我的 GitHub Profile 最终呈现为一个动态更新、信息丰富的个人技术档案。

它现在不仅包含了真实反映我学习时间的贡献图,还有一个内容详尽、格式统一、且能够通过自动化脚本持续更新的刷题记录面板

这个 Profile 将忠实地记录下我大学四年走的每一步。

欢迎访问我的➡️ GitHub 主页 ⬅️以查看最新进展与项目源码,也欢迎各位大佬前来指点~