1. 别急着背命令:Git最值钱的不是命令,而是它帮你管理变更的底层思路
我见过太多新人学Git,第一件事就是打开一篇“常用命令大全”,把add、commit、push、pull背得滚瓜烂熟,结果一进真实项目照样手足无措:有人把本地代码弄丢了不知道怎么找回,有人把半成品提交到了公共分支被同事问候,还有人合并时一看到冲突就冷汗直冒。这些问题的根源不是命令记得不够多,而是没搞明白Git到底在替你做怎样一件事。
Git从诞生那天起,解决的核心问题只有一个:让项目的每一次变化都有迹可循,并且允许多人同时在这些变化上安全地工作。它不是简单的“文件备份工具”,而是一套围绕快照、引用和对象构建的版本管理系统。我这里说的“快照”,你可以理解成给整个项目的当前状态拍一张带时间、作者和说明的照片。每次commit,Git都会记录“此刻项目里所有文件分别长什么样”,而不是只记录哪个文件改了哪一行。正因为有这个设计,它才能支持你在任意两个快照之间自由对比、跳转和回退,也能让多个分支各自生长、随时合并。
1.1 工作区、暂存区、本地仓库和远程仓库,四者到底什么关系
很多教程喜欢画图,但我建议你用送快递的逻辑去记。
- 工作区:你现在能看到的、正在编辑的文件目录,相当于你的“快递包裹堆放区”。
- 暂存区:你准备提交但还没最终打包的内容,相当于你把要寄的东西先放进一个专门的整理筐里。Git不强制你一次性提交所有改动,你可以只挑一部分文件放进去,形成一次粒度合理的提交。
- 本地仓库:你真正“打包封箱”后存放的位置,里面是一个又一个commit快照。这一步由git commit完成,提交之后就进入Git的正式版本历史。
- 远程仓库:放在服务器或代码托管平台上的仓库,是团队成员之间同步的中转站。你的本地仓库和远程仓库是两份相对独立的Git仓库,通过push和pull交换更新。
四者的关系我在日常操作里会表现为一条链:在工作区改文件,用git add放进暂存区,用git commit生成新快照,最后用git push把本地快照同步到远程。
新人最容易忽略的是暂存区的价值。它是一个完全独立的中间层,你可以只提交A文件的改动,而让B文件的改动继续留在工作区;也可以把一个文件里的一部分改动通过git add -p拆分提交。这种精细控制力,是复制粘贴式备份根本做不到的。
1.2 提交不是“保存”,是给项目拍快照
如果你以前用过SVN或者只听别人讲过“提交就是保存”,那我建议你把这句话忘掉。Git里的commit是一个完整快照,你在提交那一刻项目是什么状态,Git就完整记下什么状态。哪怕一个文件只修改了1个字符,Git也会为这个文件重新生成一份完整的内容对象,只是通过压缩存储来节省空间。
理解这一点有什么用?最大的用处是解释“为什么Git分支这么轻”。因为每次提交都保存了完整项目状态,新分支本质上只是往某个提交上加一个会移动的指针,创建一个新分支几乎不占额外空间。所以你在本地开十个八个分支完全没压力。另一个用处是,当你需要找回一个已经删掉的文件时,只要那个文件曾经被提交过,它就会一直存在于Git的对象数据库里,只是不再被任何分支引用而已。正因为提交的底层是对象,Git的“后悔药”才有那么多花样。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Git装完后的头等大事不是学命令,而是先把这三组配置改舒服
很多新手装完Git就急着clone项目,结果跑第一个commit就遇到“Please tell me who you are”,跑第一个带中文文件名的项目又遇到一串八进制转义,跑一个跨平台项目还被换行符折磨。这些问题不是Git不行,而是安装后的默认配置没有针对你的使用环境做调整。
我的建议是,装好Git之后先做一次“初始化配置”,而且这一步应该跟着使用习惯走,不是照抄别人一条条粘贴。下面这三组配置是我自己在每一台新电脑上都会优先处理的,顺序也代表优先级。
2.1 用户名和邮箱:每个commit的身份证,必须和团队规范对齐
没有设置user.name和user.email时,Git根本无法提交。即使你后面补上,已经被提交到历史中的作者信息也是很难改动的,尤其是已经推到远程公共分支的记录,改起来会牵扯到整个团队。
配置命令非常简单:
bash复制git config --global user.name "你的名字"
git config --global user.email "you@example.com"
这里有个容易被忽略的点:--global写在参数前面还是后面,大部分人习惯写成git config --global user.name "名字",这种写法没问题。但如果别人给你一段配置,看到的是git config user.name --global "名字",也能执行成功,因为Git的参数解析比较宽容。为了不给自己和其他看文档的人添堵,建议固定使用第一种顺序。
团队协作时,user.email往往会和代码托管平台的账号绑定,用于把提交记录关联到人。所以入职新公司或加入新开源项目时,先问一下团队要求的提交邮箱,不要想当然用个人邮箱。查看当前配置可以用git config --list,也可以单独查某一项:
bash复制git config user.name
git config user.email
如果发现配置错了,在还没有把提交推到公共分支之前,可以用git commit --amend --reset-author来更正最近一次提交的作者信息。已经推上去的提交就不建议走这条路了,尤其是多人共用的分支,后果是重写历史,会让其他人的本地仓库出现一堆分叉。
2.2 换行符与中文显示:两个极易让人崩溃的global配置
跨平台项目里,换行符是最容易制造“假改动”的坑。Windows下默认换行符是CRLF,macOS和Linux下是LF。如果仓库里的文件被一次提交从LF批量改成CRLF,git diff会看到整个文件都被改了,代码评审直接变成大型找不同现场。
Git提供的解决方案是core.autocrlf,我的习惯是按操作系统区分:
- Windows上建议设置:git config --global core.autocrlf true,提交时自动把CRLF转成LF,检出时再转回CRLF。
- macOS和Linux上建议设置:git config --global core.autocrlf input,提交时把CRLF转成LF,但检出时不做转换。
- 如果你的项目已经用.editorconfig或.gitattributes严格控制了换行符,可以把autocrlf设为false,完全交给代码仓库里的规则去处理。
中文文件名和中文路径在终端里显示成“\346\265\213...”的八进制转义,也是很多人第一次用Git会懵的地方。这其实是Git为了兼容旧系统的默认行为,远程仓库拿到的是UTF-8编码后的文件路径,只是显示时被转义了。把它关掉就能直接看到中文:
bash复制git config --global core.quotepath false
这个配置我的建议是直接全局设上,几乎没有任何副作用。改完之后运行git status、git log,中文文件名就能正常显示了。
2.3 用alias把高频命令缩成短指令,效率提升比想象中明显
Git命令本身不长,为什么还要配别名?因为Git的高频操作往往不是一条命令,而是固定组合。比如查看带图形的提交历史,我得敲git log --oneline --graph --decorate --all,每次敲一遍真的很烦。配置别名后一句搞定:
bash复制git config --global alias.lg "log --oneline --graph --decorate --all"
之后用git lg就能看到清晰的提交分支图。再比如我习惯用git st表示git status,用git cm表示git commit -m:
bash复制git config --global alias.st status
git config --global alias.cm "commit -m"
别名的本质是给子命令起外号,不是给整个git命令做系统级别名。所以git lg会被解释成git log --oneline --graph --decorate --all,git cm "hello"会被解释成git commit -m "hello"。这套逻辑需要在理解之后再配,不然你配了git push --force-with-lease的别名,某天忘了它背后是强推,风险会很大。
3. 日常最高频的Git操作链路:提交、分支、同步到底按什么节奏来
配置做完之后,真正决定你一天体验的是日常操作习惯。我见过不少人一整天就只做add、commit、push、pull四件事,但依然会遇到冲突、提交错分支、把临时文件推上远程的意外。问题不在于操作本身,而在于操作的先后顺序和粒度。
3.1 一次规范的提交,至少应该包含这一步检查和拆分
我推荐的本地提交流程是这样的:
bash复制git status
git diff
git add <file1> <file2>
git commit -m "描述本次改动的主题"
在add之前先跑git status和git diff,目的是确认两件事:第一,哪些文件有改动;第二,改动内容是否都符合本次提交的意图。很多人习惯直接git add .,一次性把所有改动打包提交。这个习惯在只有你一个人的实验项目里没问题,但在项目里很容易把调试日志、临时注释、本地配置一起带上。
如果某个文件里的改动可以分为两个逻辑,比如一个修复bug和一个优化文案,可以拆成两次提交。做法是用git add -p进入交互式暂存模式,Git会把文件按改动块拆开,逐个问你是否要加入暂存区。这个过程新手觉得麻烦,但养成习惯之后,回看历史提交时会非常爽。每次提交都只做一件事、只描述一件事,后面git bisect排错定位问题时,会更容易找到“究竟是哪一次提交引入了这个bug”。
提交信息也不该随便写。我见过最让人崩溃的提交信息包括“update”“fix”“aaaa”。三个月后回看历史,谁也搞不清那次提交到底改了什么。这个我在后面讲团队协作时还会展开。
3.2 分支不是用来隔离风险的,是用来降低反馈成本的
分支是Git里最值得“多用”的功能。不需要等一个功能完全成熟再开分支,我在开始实现任何有独立目标的改动前,都会新建一个分支,不管改动是一个bug修复还是一个新页面。
bash复制git checkout -b feature/login-page
# 等价的老式写法
git branch feature/login-page
git checkout feature/login-page
新建分支之后,你在主分支上的工作不会受到影响,可以随时切换回去查资料、看别的代码、改紧急bug。这里有一个很实用的技巧:在切换分支前,先把当前分支的状态收干净。要么commit,要么用git stash暂存。否则你带着一肚子未提交改动切到另一个分支,Git会尝试把这些改动迁移过去,一旦两个分支对同一文件的改动有冲突,Git会直接拒绝切换,让你先处理掉再走。
我自己的经验是,每完成一个小目标就提交一次,不必等到“完全跑通再提交”。这样的好处是每个分支上的提交历史都是渐进式的,后续如果走错了方向,可以用reset或revert回退到任意一个小节点。频繁提交会让log看起来碎,但这恰恰能保留真实的开发思路。
3.3 先把远程状态拉干净,再开始动本地代码
远程同步的正确姿势,应该是在你准备开始写代码前就做一次,而不是在写完一大坨之后才想起要拉取。推荐顺序是:
bash复制git fetch origin
git status
fetch会把远程最新状态下载到本地,但不会动你的工作区。Git会告诉你当前分支领先还是落后于远程分支,这比直接git pull安全得多。因为git pull等价于git fetch + git merge,如果你本地有未提交的改动,pull时遇到冲突会直接卡在原地,而fetch不会碰你的工作区。
等确认本地和远程没有分叉,或者分叉很小时,再考虑git pull。多人协同时我更推荐:
bash复制git pull --rebase
rebase的含义是把你本地的提交从当前基线“抽出来”,等远程的提交放好之后,再把你的提交逐个安放到远程提交之后。这样提交历史是一条干净的直线,不会出现很多merge commit织成的毛线球。对rebase不熟悉的人第一次看到历史被改写会慌,但它只作用于你尚未推送到远程的本地提交,影响范围完全可控。原则是:还没推到远程的提交可以rebase,已经推到远程公共分支的提交不要rebase。
推送时也有一个细节值得说:默认分支名可能是master,也可能是main,很多团队已经切到main了。新克隆的仓库会自动跟踪远程分支,push时不加参数也能推。但如果你的本地分支还没有和远程分支建立跟踪关系,第一次push会要求指定远程分支:
bash复制git push -u origin feature/login-page
加上-u后,本地分支和远程分支的跟踪关系就固定了,之后每次直接git push即可。
4. 免密配置的核心不是“记住密码”,而是让Git找到正确的登录凭据
热词里反复出现“git免密”,说明大家确实被反复输入账号密码折磨过。免密其实分两种场景:使用HTTPS协议时的凭据保存,和使用SSH协议时的密钥认证。很多人混淆了这两套体系,导致配了好几个小时依然不生效。
4.1 HTTPS协议免密的常见做法与坑
如果克隆地址是https://github.com/xx/repo.git,每次push都需要输入账号密码或token。这是因为Git不知道到哪去找你的登录信息。解决的办法之一是利用Git自带的凭据管理器。
Windows上安装Git for Windows时默认就会绑定Git Credential Manager,macOS上通常会使用osxkeychain。凭据管理器的作用是:第一次输入账号和token后把它安全存储在系统钥匙串里,之后Git自动读取。确认方式:
bash复制git config --global credential.helper
如果能输出manager或osxkeychain,说明凭据助手已生效。如果输出为空,可以先手动配置:
bash复制git config --global credential.helper manager
这里要提醒一点:GitHub和GitLab等平台已经不再接受账户密码进行push操作,要求使用Personal Access Token。很多人配置完凭据管理器后依然提示登录失败,多半是在输密码时还输真实的账户密码,而不是填入token。token的权限范围也要按需配置,不要贪多,避免泄露后风险过大。
4.2 SSH密钥:生成一次,用上很多年
如果你更愿意走SSH协议,克隆地址长这样:git@github.com:user/repo.git。SSH免密的原理是建立一个公钥和私钥的配对。私钥保存在本地,公钥配置到代码托管平台。此后每次连接时,Git会通过私钥证明“我就是持有这个公钥的人”。
生成密钥的标准做法:
bash复制ssh-keygen -t ed25519 -C "you@example.com"
一路回车后,会在~/.ssh目录下生成id_ed25519私钥和id_ed25519.pub公钥。公钥内容可以打印出来复制到平台:
bash复制cat ~/.ssh/id_ed25519.pub
将公钥添加到GitHub/GitLab/Gitee的SSH Keys设置页面后,可以验证连通性:
bash复制ssh -T git@github.com
第一次连接时终端会询问是否信任主机,输入yes即可。如果生成密钥时设置了passphrase,每次连接可能还要输入一次口令,想让系统代理保存口令的话可以启用ssh-agent。macOS还支持把密钥加入钥匙串:
bash复制ssh-add --apple-use-keychain ~/.ssh/id_ed25519
Linux和Windows则看你使用的终端环境,Windows下如果用了Git Bash,通常建议开启ssh-agent服务再执行ssh-add。
4.3 一台电脑管理多个Git账号,关键在主机的别名规则
很多开发者的痛点不是“不会配置免密”,而是“我同时有公司账号和个人账号,SSH Key怎么分开”。常见误区是给两个平台生成两个密钥,然后把两个都装进ssh-agent,结果连A平台时总是用B平台的私钥去验证,被反复拒绝。
更稳妥的做法是让SSH根据域名来匹配不同的私钥。在~/.ssh目录下新建一个config文件,内容大致是:
bash复制Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_github
Host gitlab.company.com
HostName gitlab.company.com
User git
IdentityFile ~/.ssh/id_ed25519_company
如果两个账号同时在同一个平台,比如一个GitHub账号用来传个人项目,另一个GitHub账号用来传公司项目,就不能只靠域名区分了。这时候需要给Host起一个别名,比如把公司账号的主机名伪装成github-work:
bash复制Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_company
然后克隆项目时使用git@github-work:username/repo.git这样的地址。这是因为Host决定了SSH连接时使用哪个私钥,而实际请求到的主机依然由HostName指定。这套配置理解之后,多账号的问题就迎刃而解了。另外还有个配套知识:SSH连接的账号名和仓库地址里的user未必就是令牌名。Host里写User git只是因为Git托管平台默认要求登录名为git,不是你的Gitee用户名,很多新手在这里卡住过。
5. 命令行里一串神秘的-c参数和--no-optional-locks,拆开看全是实操细节
有些IDE在使用Git时,会在后台输出一长串奇怪命令,比如git -c diff.mnemonicprefix=false -c core.quotepath=false --no-optional-locks status。这串命令在热词里出现频率很高,因为它看起来像“乱码指令”,实际上每段都有明确含义。学会拆解后,你以后看到任何类似命令都不会慌。
5.1 -c的本质:不修改配置文件,只对这一次命令注入临时配置
Git命令行里有一个通用参数-c,格式为-c
所以一条命令里出现多个-c,就说明外部工具想在不破坏用户既有配置的情况下,为这次命令临时指定几个配置。它比直接修改config文件更安全,也更适合程序化调用。
5.2 高频出现的那几个配置,分别管什么
核心配置之一是diff.mnemonicprefix=false。默认Git在显示diff时,会把两个比较对象标注成a/和b/前缀,比如--- a/src/index.js和+++ b/src/index.js。如果开启diff.mnemonicprefix为true,Git会用i/和w/这种助记前缀表示索引和工作树。对于IDE里的diff面板来说,稳定且统一的a/b前缀更容易被解析,所以许多GUI工具会主动在命令里加-c diff.mnemonicprefix=false,确保不会因为用户手动开了某种模式而渲染出错。
在Git从2.x版本开始,diff.mnemonicPrefix默认就是false,默认行为已经很稳定。工具仍然加这条参数,多半是出于显式申明的防御心理,防止用户环境里存在旧配置或自定义预设。
另一个是core.quotepath=false,前面介绍中文文件名时说过。IDE在后台执行git status时也希望拿到可读的中文路径,不想处理一堆八进制转义字符串,所以会把这个参数临时置为false。
与之类似的还有这些,我建议你了解但不一定每次手敲:
| 参数 | 作用 |
|---|---|
| color.ui=false | 临时关闭所有颜色输出,适合工具解析纯文本 |
| core.pager=cat | 不让git log结果进入分页器,方便程序一次性读取全量输出 |
| status.branch=true | 在status中额外显示加分分支的跟踪信息 |
| commit.gpgsign=false | 如果用户默认开启GPG签名,但当前环境没有密钥时会临时关掉签名,避免命令失败 |
5.3 --no-optional-locks:让读操作不影响写操作
Git在设计上会尽量保持命令的低副作用,但有些命令比如git status在运行时会触发一次索引刷新。这个刷新属于非必要动作,目的是让索引文件提前与工作区同步,这样后面操作更快。代价是,如果此时恰好有另一个Git命令正在写入索引,比如git commit,可能出现暂时性的“索引被锁”冲突。
--no-optional-locks的作用就是告诉Git:“这次执行status虽然平常会顺手刷新索引,但我不需要这个优化,连带可能的写锁也不要去碰。”IDE在后台高频调用git status时往往不希望自己干扰正在执行的前台写操作,所以就会加上这个参数。换句话说,这条命令是一个后台进程主动给关键路径让路的行为。
5.4 识别这些参数后,什么时候你也需要手写它们
理解这些参数不只是为了看懂IDE日志。如果你自己写脚本或CI流程,会遇到同样的场景:在不知情的时候读取用户配置,就可能因某个全局配置导致脚本解析失败。所以在脚本里用-c显式固定关键配置,反而比依赖用户环境更可靠。
一个典型场景是脚本里要比较两个分支的差异,并送给后续程序处理。如果用户的全局配置里设置了color.ui=always,输出的diff会包含ANSI颜色转义,程序解析就会出错。此时可以在脚本里加-c color.ui=false,或直接设置环境变量GIT_CONFIG_COUNT等方式。另一个场景是自动化巡检脚本里执行git status获取变更文件,为了避免和正在运行的编辑器冲突,可以加上--no-optional-locks。虽然它并不能完全避免所有并发写锁,但能把索引刷新的额外锁绕开。
6. Git的后悔药体系:reset、revert、restore到底在什么场景下用哪个
新手面对git reset、git revert、git restore时很容易晕。这三个命令都能“撤销”改动,但撤销的对象和影响的层级完全不同。我习惯把问题拆成三种场景来记忆:还没提交、已经提交但没推送、已经推送到了远程公共分支。
6.1 用restore处理工作区和暂存区的“反悔”
如果改动只存在于工作区,想让文件回到最近一次commit的样子,用:
bash复制git restore <file>
这个命令会直接丢弃工作区里尚未暂存的改动。如果改动已经git add进暂存区,但你想把暂存状态撤销,也就是把文件从暂存区退回工作区,用:
bash复制git restore --staged <file>
很多习惯老命令的人会写git reset HEAD
注意,无论是restore还是checkout,一旦改了工作区文件且没有提交,这个改动就找不回来了。所以大范围覆盖前先确认一下,或者临时用git stash打个包。
6.2 已经提交但还没推送:reset帮你回到任意节点
当最近一次提交错了,不要急着新建修复提交。可以先用git log --oneline看看提交历史,找到你想保留的目标提交,然后执行:
bash复制git reset --soft <commit>
git reset --mixed <commit>
git reset --hard <commit>
三种模式的区别在于是不是保留工作区和暂存区内容。对新手来说,我的建议是慎用--hard。--hard会连同工作区文件一起回到指定commit的状态,当前未提交的改动会全部丢掉,而且没法通过Git找回。--soft最温和,它会回到指定commit,但把期间提交过的改动全部放回暂存区,方便你重新整理提交。--mixed是默认模式,它会把改动放回工作区,暂存区被清空。
比如你连续提交了两个commit,第二提交的内容不完整且和第一个逻辑交叉,想合并成一次更完整的提交,可以:
bash复制git reset --soft HEAD~2
git add .
git commit -m "合并前两笔提交"
这样历史就更清爽了。前提是这两个commit都没被推送到远程。已经推送过的提交不要随便用reset改写,因为一旦别人把你的旧提交拉取到本地,你的重写历史会让远程和本地出现分叉。
6.3 已经推到远程:优先用revert生成反向提交
如果错误的提交已经推送到了公共分支,怎么办?标准答案是用git revert。
bash复制git revert <commit>
git revert的作用是生成一次新提交,把目标commit的改动反向应用回去。它可以理解成“用一笔正向提交把这笔变更抵消掉”。它不会改动已有提交历史,所以不会破坏其他人基于旧提交所做的工作。
当多个提交缠在一起需要回退时,可以指定范围:
bash复制git revert --no-commit HEAD~3..HEAD
git commit -m "revert最近三笔改动"
用--no-commit先把反向改动累积到暂存区,确认没问题再提交。git revert支持同时revert多条提交,但处理顺序有讲究,冲突也多,遇到复杂情况时不要图快,逐条解决比一次性revert更稳。
6.4 四类后悔操作对比
| 场景 | 命令 | 影响范围 | 是否改写历史 |
|---|---|---|---|
| 丢弃工作区未暂存改动 | git restore | 单个文件 | 否 |
| 取消暂存态 | git restore --staged | 单个文件 | 否 |
| 回退最近提交并保留改动追加到新提交 | git reset --soft | 提交历史 | 改写本地历史 |
| 回退提交并删除期间所有工作区改动 | git reset --hard | 提交历史+工作区 | 改写本地历史 |
| 抵消已推送提交,追加反向提交 | git revert | 远程历史 | 否,追加新提交 |
7. 团队协作里那些看不见的规则:分支策略、提交信息、冲突处理
单人的Git操作只要技术上不出错就行,团队协作就复杂得多。它的复杂度不在命令本身,而在于每个人对“如何协作”的预设不同。有的团队喜欢往main分支上直接推送,有的团队要求所有改动都走合并请求;有的项目用Git Flow把分支分得很细,有的项目用GitHub Flow一个功能一个分支。分支策略没有绝对最优,要看团队的发布节奏和规模。
7.1 至少应该约定:主干分支保持可发布,功能分支随建随合
我比较推荐的简单策略是:main分支永远保持干净可部署;新功能从main切出feature分支;开发完成后通过合并请求评审合回main;发布时从main打tag。这样规则简单,新成员也容易理解。
在项目起步阶段,不要急着套重型Git Flow,因为develop、release、hotfix这类长期存在的分支一旦配合不好,会让新手搞不清“我到底该基于哪个分支开发”。等团队规模变大、发布流程明确后,再引入多层分支也不迟。
另一个容易忽略的默契是:不要让分支活得比一个迭代还长。分支长期不合并,等到要合回去时,往往已经积累了海量冲突,而开发者也忘了当时的上下文。与其硬憋一个大功能分支,不如把功能拆小,做一点合一点。
7.2 提交信息的可读性,决定半年后的维护效率
提交信息写得好不好,直接影响代码评审和后期排查。我给自己定的模板是:
text复制<type>: <subject>
<optional body>
type可以是fix、feat、refactor、docs、test等;subject是对本次改动的一句话描述;如果需要更多背景,放在空行后的body里。例如:
bash复制git commit -m "fix: 修复登录页在移动端输入框被键盘遮挡的问题" -m "原因是fixed定位的元素未监听窗口高度变化"
Git命令里多个-m表示多段内容,对应模板里的subject和body。这样提交历史用git log --oneline看时是一系列清晰标题,需要细看时再用git log展开完整信息。
这里插一个讲给新手的经验:提交信息的时态和语态不用纠结英文还是中文,团队统一才是关键。中文项目的提交信息用中文完全没问题。怕的是中英混杂、风格随意,回看历史时像翻草稿纸。
7.3 冲突处理:不要慌,冲突文件只是需要你做一次手动合并
冲突本身不是灾难,它是Git在保护你的代码:当两个人恰好改了同一段逻辑,Git不知道谁对谁错,只能停下来问你。我第一次遇到冲突时也手忙脚乱,后来总结了一个比较稳的流程。
先检查冲突范围:
bash复制git status
有冲突的文件会被标记为both modified。逐个打开这些文件,找被<<<<<<<、=======、>>>>>>>包围的区域,这些标记恰好对应了当前分支和合并进来的分支对同一位置的两种改动。你需要做的是判断保留哪一边、还是两边都留、还是结合后写一段新代码。处理完删除这三个冲突标记,然后git add这个文件,最后执行git commit结束合并。
如果合并到一半发现自己处理不了,可以用git merge --abort直接回滚到合并前状态。这里要特别强调:不要在合并冲突中用git checkout .或git reset --hard来“解决冲突”,它们会把冲突状态和你的本地改动一起丢掉,有可能把别人代码也带没。最危险的习惯是遇到冲突就找同事的手机号,然后自己一通乱敲。
最后一个容易被忽略的习惯:动大手术前先留一个可回退的临时提交
讲真,回到这套Git使用经验最核心的一点,我特别想分享的习惯是:在做大规模重构、批量替换、删除文件前,先提交一个临时commit,或者至少给当前状态打上一个tag。我吃过太多次亏,自以为完全理解改动,结果改完发现方向错了,只能靠Git来救命。
如果你不放心commit,可以使用轻量tag作为一个临时锚点:
bash复制git tag backup-before-refactor
这个tag会永远钉在当前提交上,后面你无论reset还是rebase,都可以随时找回来:
bash复制git checkout backup-before-refactor
如果担心自己不经常维护tag导致堆积,用一个临时分支也行:
bash复制git branch backup/2025-xx-xx
我不会过度强调什么“最佳实践”,但我真的觉得,所有Git命令里,最厉害的不是那些花哨的参数组合,而是“让每一次操作都能安全退回”的意识。很多看起来玩得很花的人,并不是记住了更多命令,而是他们对每次操作的影响范围心里有数:这条命令会不会改写历史,会不会丢工作区,会不会影响同事,以及哪种情况下应该先停手。这种意识比背下一百条命令实战价值更高。
