单分支仓库处理¶
先不考虑管理员对git仓库的裁剪,直接git clone后,对于大仓库小硬盘的电脑而言,多项目开发时有点扛不住,因此每个基线对应「一个文件夹,一个独立仓」是常见的需求。
在整个手机工程等超大工程的处理时,--depth=1也是常用的参数。w
容易误解的命令¶
git pull origin branchA :
fetch命令¶
模式 1:无参数 fetch(git fetch / git fetch origin)
严格遵守 remote.origin.fetch 配置清单。清单里没有的分支,不会自动同步,不会生成持久 origin/xxx。也就是大家常说的「single-branch 限制」来源。
模式 2:显式传入 refspec git fetch origin feature/all/test_git
显式 refspec 不会复用 remote.origin.fetch 里配置的映射,默认只会填充 FETCH_HEAD。
⚠️ 绕过本地所有 remote.origin.fetch 配置,不受单分支限制!Git 直接向远端发起请求:我要拿 refs/heads/feature/all/test_git。这条语法本身不需要你预先在 config 添加对应规则。
模式 3:git fetch origin "+refs/heads/feature/all/test_git:refs/remotes/origin/feature/all/test_git"
refs/heads/feature/all/test_git→ 远端分支refs/remotes/origin/feature/all/test_git→ 本地的远程跟踪分支
模式 3:显式完整 refspec git fetch origin "+refs/heads/feature/all/test_git:refs/remotes/origin/feature/all/test_git"
等价于在本次命令中临时追加一条 remote.origin.fetch 规则,不受 single-branch 限制,会生成/更新持久的远程跟踪分支 origin/feature/all/test_git,效果相当于把该分支纳入「模式 1」的清单(但仅本次生效,不写回 config)。
refs/heads/feature/all/test_git→ 远端分支refs/remotes/origin/feature/all/test_git→ 本地的远程跟踪分支- 前缀
+表示允许非快进更新(强制覆盖本地跟踪分支)--- title: Git for Windows
单分支仓库处理¶
先不考虑管理员对git仓库的裁剪,直接git clone后,对于大仓库小硬盘的电脑而言,多项目开发时有点扛不住,因此每个基线对应「一个文件夹,一个独立仓」是常见的需求。
容易误解的命令¶
误解1: git pull 只从服务器中获取当前分支内容 正解:从refspec所指定的分支拉取内容,默认clone形式下获取的是所有提交内容。
误解2:git push因冲突失败时,git pull会产生merge节点。为了让分支始终保持fast forword形式,只能手动revert → pull → cherry-pick 来处理。
正解:没必要那么复杂。git push 默认拒绝非快进推送。因此只需要配置一次git config pull.rebase true,以后只需要git pull + git push就行 (合理设计分工形式,基本不需要解冲突)。
fetch命令¶
模式 1:无参数 fetch
命令:git fetch / git fetch origin
严格遵守 remote.origin.fetch 配置清单。清单里没有的分支,不会自动同步,不会生成持久 origin/xxx。也就是大家常说的「single-branch 限制」来源。
模式 2:显式传入 refspec
命令: git fetch origin feature/all/test_git
显式 refspec 不会复用 remote.origin.fetch 里配置的映射,默认只会填充 FETCH_HEAD。FETCH_HEAD 生命周期:下一次 fetch /pull 就会被覆盖。
绕过本地所有 remote.origin.fetch 配置,不受单分支限制!
Git 直接向远端发起请求:我要拿 refs/heads/feature/all/test_git。这条语法本身不需要你预先在 config 添加对应规则。
若要创建本地分支,还要git checkout -b feature/all/test_git FETCH_HEAD,注意这个没有绑定upstream的分支。
模式 3:临时扩展 fetch 规则并指定本地远程跟踪分支:
命令:git fetch origin "+refs/heads/feature/all/test_git:refs/remotes/origin/feature/all/test_git"
refs/heads/feature/all/test_git→ 远端分支refs/remotes/origin/feature/all/test_git→ 本地的远程跟踪分支
模式 4: 长期跟踪
- refspec 一次性跟踪:
git config --add remote.origin.fetch "+refs/heads/feature/all/test_git:refs/remotes/origin/feature/all/test_git" - 后续自动拉取:
git fetch - 创建本地分支:
git checkout -b feature/all/test_git origin/feature/all/test_git
merge¶
git merge 不一定产生新的合并提交(merge commit),分两种模式:快进合并(Fast-forward) vs 真正三方合并(生成新 commit)。
快进合并:
当本地分支顶端 和 远端上游 origin/foo 是线性后继关系,本地没有新增提交。 Git 不会创建任何新提交,仅仅移动本地分支指针到远端 commit。 日志看不到任何合并节点。
三方合并,产生 Merge Commit(出现分叉)
拓扑:
远端往前提交 C,你本地在 B 基础上提交 D,历史分叉。
此时 git merge origin/foo 找不到一条直线,必须生成新的合并提交,同时指向 C 和 D 两个父节点。
git log 能看到带有两个 parent 的 merge 节点。
也可以强制生成节点:git merge --no-ff origin/xxx
pull¶
git pull
# 当git config pull.rebase无输出,等价展开
git fetch origin
git merge @{upstream}
# 当git config pull.rebase = true,等价展开
git fetch origin
git rebase @{upstream}
默认是merge形式,推荐修改为rebase形式。
- merge pull:远端新提交、本地提交并排,新增合并节点包裹两者
- rebase pull:远端更新历史放前面,你的本地提交全部追加到整条历史最后。
git pull卡住: 把.git/config文件中的url协议从ssh改为https协议。
回车换行维护¶
使用只靠 autocrlf 其实是粗糙方案,适合新手;规范团队不会单纯依赖它。主要还是太容易混乱了。.gitconfig 在每个人电脑上独立配置,不能保证每个人配置一样。而且core.autocrlf 是一刀切全局策略,无法区分文件类型。
没有在 .gitattributes 指定 eol 且 autocrlf 为false的情形, 会 fallback 使用 core.eol,可选值:lf / crlf / native(native = 当前操作系统默认换行)。个人认为一旦有文件落地core.eol的判断也是很危险的。
考虑到.gitattributes 后面匹配上的可以覆盖前面的内容,一般在最前面写入* -text
推荐.gitattributes配置:
完整
.gitattributes已迁到自托管 git 仓库 tool-skills,可直接查看 / 下载:
→ git-config/.gitattributes · 查看(raw 下载)
提交补救¶
1、如果是最新一笔提交需要修改,直接在工作区修改,正常git add, 只是提交命令从git commit 变为git commit --amend而已。如果只是提交信息需要修改,不涉及提交内容的变化,直接amend即可。
2、大需求按模块提交后,如果还要修改之前的提交,则需要git stash 和 git rebase -i HEAD~<n> 组合使用。当然这种改动,交给大模型会高效一些。
3、提交不小心git reset --hard HEAD^<n>掉了,不用担心找不到hash,结合git reflog信息进行cherry-pick即可。
钩子¶
pre-commit 需考虑的检查¶
仅处理已暂存文件(git diff --cached --diff-filter=ACMR)。
- 提交者身份校验:
user.name与user.email命中约定格式且互相一致(email 前缀 == user.name)。域名与正则置于脚本顶部可配置,不绑定特定企业。 - 工具依赖检查:确保
python(≥3)、clang-format、black(可选isort)及所需 Python 模块(chardet、json、jsonschema等)已安装,缺失即退出。 - 大文件检测:拦截超过大小阈值(如 > 10MB,可配置;可按类型细分,如
.zip/.exe/.dll/媒体)的文件入库,避免大二进制不可逆地撑大仓库历史,提示改用 Git LFS 或外部存储。pre-push 可作兜底,拦截被--no-verify绕过、已进入本地历史的超大文件推送。 - 编码与行尾归一化:统一为 UTF-8 with BOM + LF,消除 GBK / 无 BOM / CRLF 混入。
- 代码格式化:对暂存文件按语言就地格式化。
- C/C++:
clang-format -i -style=file,覆盖.cpp/.cxx/.cc/.c/.h/.hpp/.inl/.i,样式由仓库.clang-format决定。 - Python:
black(可选配合isort整理 import 顺序),覆盖.py。 - 版本一致性(关键):格式化工具的不同版本对同一配置可能产出不同结果(
clang-format、black均如此),造成"格式化漂移"与无意义 diff。对策任选其一:校验工具版本(不符即拒绝并提示安装对应版本);或将版本固定在仓库内——C/C++ 可托管约定版clang-format二进制,Python 可在requirements.txt锁定black/isort版本并由钩子在固定 venv 中调用。 - 格式化产生改动时自动重新
git add对应文件,让格式化后的内容进入本次提交节点;否则git commit提交的是暂存区里的旧内容,格式化未生效(改动只留在工作区)。 - 代码复杂度检测:对暂存变更中的函数检测圈复杂度 / 认知复杂度(工具如
lizard、radon),超阈值(如认知复杂度 > 15)即报告。只对本次新增的函数报复杂度;修改既有函数时,若改动前已超阈值(既有技术债)则不报,仅当本次改动使其"由达标变为超标"才报,避免历史债淹没新增问题。实现上以git diff --cached识别新增函数定义行(+行),仅对这些函数跑复杂度分析。 - 配置文件 JSON 校验:按路径匹配 schema 校验字段类型、必填项、枚举值,阻止非法配置入库。
pre-push 需考虑的检查¶
逐行读取推送的 <local_ref> <local_sha> <remote_ref> <remote_sha>,对每个 remote_ref 匹配命名白名单。
- 分支命名白名单:
feature/{module}/{name}、hotfix/{module}/{id}、release/vN、主干 / 集成分支、标签等;命名约束以正则表达(如 feature 须带模块子路径、仅小写字母数字下划线,hotfix 仅数字下划线)。 - 项目专属分支:以
project/.*之类兜底正则收敛,不在规范中固化具体项目名。 - 失败提示:未命中白名单时打印示例分支名引导改名。
- 提交信息格式复核(兜底):pre-push 可遍历推送范围
remote_sha..local_sha内的提交做信息格式复核,作为commit-msg(逐提交校验,可被--no-verify绕过)的兜底。须兼容git revert产物——revert 提交首行为Revert "<原始主题>",不匹配常规[TYPE|MODULE|LABEL]SUBJECT等首行格式,应按^Revert "豁免;commit-msg同理需豁免,否则git revert在提交阶段即被拦截、根本无法生成产物。
设计原则¶
- 只拦机器能判定的(编码、格式、schema、命名);主观问题交评审。
- 失败即阻断并给可操作提示,不静默放行。
- 幂等:对已合规文件为 no-op,多次运行结果一致。
- 离线、最小依赖(python3 + clang-format)。
- pre-commit 管内容质量,commit-msg 管提交信息格式,pre-push 管分支治理并作提交信息复核兜底,职责分离。
- 组织相关项(域名、正则、白名单)集中可配置,便于 fork / 复用。
常用命令¶
查询¶
git fetch --all && git branch -a --contains [commit hash]来查找包含该commit的分支
显示特定分支与其他分支的关系
git show-branch --contains <feature-branch>
若以前提交的人已经导致了空格问题,则: git diff --ignore-space-at-eol > ~test.diff git checkout . git apply --ignore-whitespace \~test.diff
cherry-pick 执行人和仓库的作者不一致。git show --format=full <hash>
一行显示:git log --pretty="%h %cd %an ==>%s" --date=short
提交¶
如果确实需要要新分支merge进主线上时,最好主线拉取到最新的,并把新分支rebase过去。在rebase时接冲突比在merge时解冲突好,git tree更容易理解。
cherry-pick 添加来源注释:git cherry-pick -x <Hash>
清洗¶
首先把.gitignore维护好,这个文件登记的是不被提交的内容,如部署内容和编译产物。
git reset不会清除这部分内容,只会处理git 跟踪的内容,- 只清除工作区暂存区:
git reset --hard HEAD - 撤最新的一次提交:
git reset --soft HEAD^ - 只保留git跟踪的内容(git clone下来干净的环境):
git clean -dfx && git reset --hard HEAD