Repository navigation
Expand file tree
/
Copy path4_1_git.py
More file actions
298 lines (268 loc) · 11.3 KB
/
Copy path4_1_git.py
File metadata and controls
298 lines (268 loc) · 11.3 KB
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
================================================================
第四阶段:开发环境与团队协作
4.1 版本控制(Git)
================================================================
【课程属性】L1/L2 · 必修 · 预计 60~120 分钟
【前置要求】完成 3.3 HTTP 基础;3.4 并发编程不作为前置
【学习目标】
1. 掌握 Git 核心命令(clone, add, commit, push, pull)
2. 掌握分支管理(branch, merge, rebase)
3. 理解 Git 的存储模型(blob/tree/commit)
4. 掌握 GitHub/GitLab 协作流程(PR)
5. 学会常见错误恢复
【本课 Python 知识点】
- subprocess 调用 git 命令 - 用代码模拟 Git 内部结构
- .gitignore 规则 - git 常用别名
【参考链接】
Git 官方文档: https://git-scm.com/doc
交互式学习: https://learngitbranching.js.org/
"""
# ============================================================
# 第一部分:Git 核心概念与存储模型
# ============================================================
#
# 三个区域:
# 工作区(working dir):你看到的文件
# 暂存区(staging):git add 后的区域,等待提交
# 仓库(repository):git commit 后的提交历史
#
# 流向:工作区 → git add → 暂存区 → git commit → 仓库
# 配合图解看:https://learngitbranching.js.org/
# 用 Python 理解 Git 的存储模型:
# Git 保存的不是"文件差异",而是"快照(snapshot)":
# 每个 commit 保存一个 tree(目录树)
# tree 由 blob(文件内容)和子树组成
# 内容相同的文件只存一份(通过 hash 去重)
# 用 dict 模拟 Git 的内部结构:
blobs = {} # hash → 文件内容
trees = {} # hash → 目录结构
commits = [] # commit 列表
def git_snapshot_demo(files: dict) -> dict:
"""
模拟 git commit 的"快照存储"。
参数: files = {"hello.py": "print('hi')", "README.md": "# 说明"}
返回: {"blobs": {...}, "tree": {...}, "commit": {...}}
"""
import hashlib
# 1. 文件内容 → blob(用 SHA-1 哈希做地址)
new_blobs = {}
tree_entries = {}
for filename, content in files.items():
blob_hash = hashlib.sha1(content.encode()).hexdigest()[:8]
new_blobs[blob_hash] = content
tree_entries[filename] = blob_hash
# 2. 目录结构 → tree
tree_hash = hashlib.sha1(str(tree_entries).encode()).hexdigest()[:8]
# 3. tree → commit(commit 指向 tree + 父 commit)
parent = commits[-1]["hash"] if commits else None
commit = {
"hash": hashlib.sha1(f"{tree_hash}{parent}".encode()).hexdigest()[:8],
"tree": tree_hash,
"parent": parent,
}
return {"blobs": new_blobs, "tree": tree_entries, "commit": commit}
# 关键理解:commit 就像"时间胶囊"——记录了那一刻所有文件的完整状态。
# 所以 git 可以随时 checkout 任意历史版本。
# ============================================================
# 第二部分:日常操作
# ============================================================
#
# 完整提交流程:
#
# 1. 初始化或克隆
# git init # 在当前目录初始化
# git clone <url> # 克隆远程仓库
#
# 2. 检查状态
# git status # 查看哪些文件改了(红色=未暂存,绿色=已暂存)
# git diff # 查看工作区具体改动内容
# git diff --staged # 查看已暂存的改动
# git log --oneline # 查看提交历史(一行一个)
#
# 3. 暂存和提交
# git add file.py # 暂存单个文件
# git add . # 暂存所有改动
# git commit -m "feat: 添加登录功能"
#
# 4. 推送和拉取
# git push origin main # 推送到远程
# git pull origin main # 先 fetch,再按配置 merge、rebase 或仅快进
#
# 提交信息规范(团队协作的礼仪):
# feat: 新功能 fix: 修复bug docs: 文档
# refactor: 重构 test: 测试 chore: 杂务
# 例:"fix: 修复用户登录时 token 过期崩溃的问题"
# 用 subprocess 在 Python 里执行 git(写自动化工具会用到):
# import subprocess
# def git_log():
# result = subprocess.run(
# ["git", "log", "--oneline", "-5"],
# capture_output=True, text=True,
# )
# print(result.stdout)
# ============================================================
# 第三部分:分支管理
# ============================================================
#
# 分支 = 并行开发线。团队协作的核心。
# 创建和切换
# git branch feature-login # 创建分支
# git checkout feature-login # 切换到该分支
# git checkout -b feature-login # 创建并切换(一步到位)
# git branch -a # 查看所有分支
# git branch -d feature-login # 删除已合并的分支
#
# 合并(merge):把分支合回主分支
# git checkout main # 先切回主分支
# git merge feature-login # 合并
#
# 变基(rebase):重新基于最新主干
# git checkout feature
# git rebase main # 把 feature 的提交"重放"到 main 最新处
# 用代码模拟分支指向(帮助理解"分支只是一个指针"):
class BranchDemo:
"""模拟 git 分支的本质:分支 = 指向 commit 的可移动指针"""
def __init__(self):
self.commits = []
self.branches = {"main": None} # 分支名 → commit
self._counter = 0
def commit(self, message):
self._counter += 1
commit = {"id": self._counter, "msg": message}
self.commits.append(commit)
# 当前分支指针前移(默认 main)
self.branches["main"] = self._counter
def branch(self, name):
# 新分支指向当前 main 的位置——没有复制任何东西!
self.branches[name] = self.branches["main"]
# demo = BranchDemo()
# demo.commit("c1"); demo.commit("c2")
# demo.branch("feature-login") # feature 和 main 指向同一个 commit
# print(demo.branches) # {'main': 2, 'feature-login': 2}
# 创建分支是 O(1) 的——只记录一个指针,不复制文件!
# 课后练习会用已经学过的 Python 结构模拟这些概念:
# dict(staging) 创建暂存区的浅层快照,本题的文件名和内容都是字符串
# history[::-1] 用切片得到倒序提交历史
# {分支名: 提交号} 表示每个分支指向哪个提交
# commit 保存快照后应把暂存区替换为空字典;不能把同一个可变字典直接存进历史。
# merge vs rebase(常见协作问题):
# merge:保留分叉历史,产生合并提交(merge commit)——真实历史
# rebase:历史线性化,像"没有分叉过"——历史整洁
# 团队规范一般:自己分支用 rebase 同步主干,合回主干用 merge
# ============================================================
# 第四部分:冲突解决
# ============================================================
#
# 冲突 = 两个人改了同一文件的同一区域。Git 无法自动合并。
#
# 冲突时的提示:
# <<<<<<< HEAD
# 主分支的内容
# =======
# 我的分支的内容
# >>>>>>> feature-login
#
# 解决步骤:
# 1. 打开冲突文件,手动决定保留哪部分(或都保留)
# 2. 删除 <<<<<<< ======= >>>>>>> 标记
# 3. git add 该文件
# 4. git commit(merge 冲突)或 git rebase --continue(rebase 冲突)
#
# 避免冲突的最佳实践:
# 1. 经常 pull(不要积累大量未同步的改动)
# 2. 每次只改自己负责的文件
# 3. 大改动尽早拆 PR(减小冲突面)
# 4. 不要格式化别人的代码(格式化会产生大量 diff)
# ============================================================
# 第五部分:远程协作(PR 流程)
# ============================================================
#
# 一种常见的 GitHub/GitLab 协作流程:
#
# 1. 从远程克隆 + 建功能分支
# git clone <repo-url>
# git checkout -b feature/my-task
#
# 2. 写代码,多次提交
# git add .
# git commit -m "feat: 实现订单查询"
#
# 3. 推送分支到远程
# git push -u origin feature/my-task
#
# 4. 在 GitHub/GitLab 网页上发起 Pull Request(PR)/ Merge Request(MR)
# - 描述:做了什么、怎么测试、截图
# - 邀请同事 review
#
# 5. Review 通过后合并到主干
# - 网页上点 Merge,或 git merge
#
# 6. 删除分支,保持仓库干净
# git branch -d feature/my-task
# git push origin --delete feature/my-task
#
# 思考:为什么要有 PR 而不是直接 push 到主干?
# 1. 代码审查:别人帮你发现 bug 和设计问题
# 2. 责任明确:谁改的、为什么改,都有记录
# 3. CI 检查:提交后自动跑测试和 lint(4.3 课内容)
# ============================================================
# 第六部分:常见错误恢复
# ============================================================
#
# 误改文件想还原:
# git checkout -- file.py # 丢弃工作区改动(危险!不可恢复)
# git restore file.py # 新版写法,等价
#
# 误 commit 想撤回:
# git reset --soft HEAD~1 # 撤销 commit,保留改动(最常用)
# git reset --hard HEAD~1 # 撤销 commit 且丢弃改动(危险!)
#
# 误 add 想撤销暂存:
# git reset HEAD file.py # 或 git restore --staged file.py
#
# 改错分支想移动:
# git stash # 暂时藏起工作区改动
# git checkout correct-branch
# git stash pop # 恢复藏起的改动
#
# 提交信息写错了:
# git commit --amend -m "正确的信息"
#
# 误删了没提交的文件(唯一救法):
# 无法恢复!所以重要文件要勤 commit
#
# 恢复操作原则:
# 1. 不确定就先用 git status / git log 看
# 2. --hard 和 --force 慎用(会丢东西)
# 3. 不把密码或密钥提交进 Git;用本课前文介绍的 .gitignore 排除本地配置
# ============================================================
# 第七部分:.gitignore 与别名
# ============================================================
#
# .gitignore = 告诉 Git 哪些文件不要跟踪(4.2 课详讲):
# .venv/ # 虚拟环境
# __pycache__/ # Python 缓存
# .env # 密钥!绝不能进 git
# *.log # 日志
#
# 设置别名(提高效率):
# git config --global alias.st status
# git config --global alias.co checkout
# git config --global alias.cm "commit -m"
# git config --global alias.lg "log --oneline --graph --all"
# 之后:git st / git co / git lg
# ============================================================
# 本课小结
# ============================================================
#
# [ ] 能走完 add/commit/push/pull 完整流程
# [ ] 会创建分支并 merge
# [ ] 会解决合并冲突
# [ ] 知道 PR 协作流程
# [ ] 知道"分支是指针"和快照存储
# ============================================================
# 课后练习(见 exercises/4_1_git.py)
# ============================================================