
1. 提交前的第一道门槛环境与仓库准备1.1 安装Git与三件套配置很多新手拿到Git的第一步不是写代码而是被安装和配置劝退。Git的安装本身不复杂各个系统都有对应方案Windows推荐直接去官网下载安装包一路Next就行记得把Git Bash Here和Git GUI Here这两个右键菜单加上后面用起来会顺手很多macOS可以用Homebrew一条命令brew install git或者装Xcode Command Line Tools自带的版本Linux各家发行版走各自的包管理器Ubuntu/Debian执行sudo apt install gitCentOS/RHEL执行sudo yum install git。安装只是万里长征第一步真正让Git能正常说话的是安装后的三件套配置。打开终端执行git config --global user.name 你的名字 git config --global user.email 你的邮箱 git config --global core.editor code --wait这三个配置缺一个你的commit记录里就会出现一串乱码或者空值别人用git blame追溯代码时根本找不到责任人。我见过很多团队项目里的提交记录作者栏写着unknown或者空白就是因为当年装完Git没配名字和邮箱后面全成了悬案。这里有个容易被忽略的细节user.name和user.email会写进每次提交的元数据里一旦提交并推送到远程仓库改起来非常麻烦。公司项目和个人项目的身份如果不同建议在各自的项目目录下用git config --local单独配置别拿全局配置一把梭。我之前就吃过这个亏在公司仓库里用个人邮箱提交了好几次后来花了不少力气才把历史记录修正过来得不偿失。1.2 换行符、中文文件名与.gitignore最容易踩的三个坑如果说三件套配置是让Git能跑的门槛那换行符问题就是让跨平台协作不炸的关键。Windows默认用CRLF回车换行作为行尾符而Linux和macOS用LF换行。如果团队里有人用Windows有人用macOSGit会自动帮你把换行符转来转去结果就会产生一种非常恶心的现象你只改了一行代码但git diff显示整个文件都变了因为Git把所有制表符、回车符都识别成了变更。我的建议是统一用LF提交前让Git做自动转换。具体做法是在项目根目录建一个.gitattributes文件写上固定规则* textauto *.js text eollf *.ts text eollf *.json text eollf *.md text eollf这个文件一旦提交团队所有成员都会自动遵循比让每个人手动改core.autocrlf配置可靠得多。Windows用户在安装Git时如果被问到Checkout as-is, commit as-is还是Checkout Windows-style, commit Unix-style建议选第二个Commit Unix-style配合上面的.gitattributes就能彻底解决换行符问题。中文文件名乱码也是个高频问题。Git在非UTF-8环境下会把中文文件名转成八进制转义序列看起来就是一堆\346\226\207\344\273\266这样的乱码。解决办法是设置git config --global core.quotepath false这样中文文件名就能正常显示了。另外建议所有文本文件统一用UTF-8编码别用GBK不然代码里的中文注释、字符串迟早变成乱码。.gitignore是另一个入门必踩的坑。很多人一开始不知道要配这个文件结果把node_modules、target、__pycache__、.idea、.vscode这些依赖目录和IDE配置全提交到了仓库里。等clone下来一执行依赖安装版本冲突、目录权限错乱各种问题接踵而来。正确的做法是项目初始化那一刻就建好.gitignore把不该提交的目录和文件全部拦住。2. Commit Message的规范与价值2.1 为什么Commit Message决定提交质量如果让我投票选Git使用中最被低估的能力Commit Message的写作绝对排第一。很多团队管理得看似严格Code Review、CI/CD、单元测试全都上了但打开git log --oneline一看提交信息全是update、fix、改改、commit这种毫无信息量的字眼整条提交历史就像一部没有标题的电影不看代码你根本不知道每一帧在讲什么。Commit Message的价值体现在三个层面。第一是排查问题的效率线上出了Bug你用git bisect做二分定位或者直接翻git log --oneline找嫌疑提交如果每条提交都写清楚了做了什么、为什么做你顺着标题就能快速缩小范围第二是Code Review的质量Reviewer在审核一个提交时如果Commit Message写得好他可以先理解意图再读代码而不是对着几百行diff猜作者的思路第三是历史追溯的可靠性半年后你接手一个旧项目想搞清楚某个模块为什么这么设计翻到当初那条带详细说明的提交比去问已经没有耐心的老同事靠谱得多。我用过一个真实例子来说明。同样是修改登录接口的Bug糟糕的提交信息是fix优秀的提交信息是fix(auth): 修复登录接口在Token过期后返回404的问题。前者你还要打开diff才能知道改了哪个文件、动了什么逻辑后者一眼就能看出修改范围、影响模块和问题现象甚至直接能用来生成发版说明。2.2 Conventional Commits规范一种行业共识的提交格式Commit Message该怎么写才算规范社区目前最主流的共识是Conventional Commits也就是约定式提交。它的核心格式是type(scope): subject BLANK LINE body BLANK LINE footer格式本身不复杂但每一部分都有讲究。type是提交类型必须从下面这些类别中选择不能自己随便发明type含义典型场景feat新功能新增一个接口、一个新的页面模块fix修复Bug修复某功能报错、数据异常docs文档变更修改README、API文档、注释style格式调整调整缩进、补分号、格式化代码不改变逻辑refactor重构调整代码结构但不改变外部行为perf性能优化提高加载速度、优化查询效率test测试相关新增用例、修复测试脚本chore构建或辅助工具变动升级依赖、修改CI配置、更新.gitignorebuild构建系统相关修改webpack、vite配置revert回滚提交使用git revert后自动生成scope是影响范围可以是模块名、组件名、服务名比如feat(user)表示影响用户模块。subject是简短标题一句话概括这次提交做了什么一般不超过50个字符祈使句、现在时比如添加用户注册接口而不是添加了用户注册接口或者用户注册接口已添加。正文部分用来补充为什么——为什么这么做、解决了什么问题、有没有副作用。这是很多人忽略的地方。举个实际的例子fix(auth): 修复Token过期后接口返回401的问题 用户在Token过期后刷新页面部分接口会直接返回401导致页面白屏。 根因是前端在请求拦截器里没有统一处理401响应刷新Token后没有重放原请求。 解决方案在拦截器中保存pending请求刷新Token成功后按队列重放。这条提交的正文交代了问题现象、根因分析、解决方案三个关键信息将来无论是排查回归问题还是做技术复盘都能提供充分的上下文。footer用来写关联的Issue编号或Breaking Changes比如Closes #1234表示关闭issue 1234BREAKING CHANGE: 修改了Login接口的请求参数结构表示不兼容变更。2.3 写Commit Message的实操技巧说为什么而不是改了什么我在实际写Commit Message时有一个原则标题说做了什么正文说为什么这么做如果代码改得很直观正文甚至可以省略但为什么这个信息不能省。比如你修复了一个Bug标题写fix: 修复日期格式化在时区为UTC时偏差8小时的问题正文补充原因是new Date(2024-01-01)会被解析为UTC时间在东八区显示为前一天改用dayjs的utc模式解析解决。这样一段正文比光秃秃的标题有价值得多。还有一个常见的争议Commit Message用中文还是英文我的建议是看团队约定最好全团队统一。中文的好处是可读性高所有人都能看懂英文的好处是和Git官方工具链兼容性最好很多代码生成工具、Changelog插件对英文格式支持更完善。但最怕的就是中英夹杂今天写update明天写修复问题后天写fix bug这种混乱状态比纯中文或纯英文都难维护。写标题的时候还有个技巧用如果这个提交回滚了你能不能用标题向别人解释发生了什么来检验。如果你自己都觉得解释不清说明标题太笼统需要拆分成多个提交或者补充正文。3. 提交粒度的艺术让每次提交都成为一件事3.1 原子性提交可回滚、可定位、可Review原子性提交是Git提交实践中仅次于Commit Message的核心原则。这个概念听起来很学术其实理解起来很简单一次提交只做一件事。这个一件事可以是一个功能的完整实现、一个Bug的连带修复、一次文档的批量更新但不能是一个文件夹里所有改动的大杂烩。我见过最典型的反面案例是开发者在实现功能A的时候顺便改了两个和A无关的变量名、删了一段无用代码、还更新了一个依赖版本最后用git add .一把梭全提交了。后果是Reviewer看diff时无法聚焦搞不清楚哪些改动是核心逻辑、哪些是附带调整如果功能A上线后出问题要回滚连带把代码优化和依赖升级也一起回滚了以后排查历史时git blame定位到的提交信息对你理解这段代码毫无帮助。原子性提交的好处用一句话总结就是让每次提交都成为独立可理解的最小单元。功能A坏了回滚功能A不会波及B和CReview时只看和本提交目标相关的改动效率翻倍将来做git bisect定位性能问题时清晰的边界能让二分查找很快锁定罪魁祸首。我在团队里推行原子性提交后最直观的感受是代码审查的争议变少了因为大家都聚焦在同一个逻辑块上讨论的深度完全不一样。3.2 用暂存区控制提交内容git add -p 和它的朋友们要实现原子性提交核心武器就是Git的暂存区。很多人对Git的理解停留在git add 添加文件、git commit 提交这个最粗粒度的层面完全没意识到暂存区允许你精确到行地选择要提交的内容。场景是这样的你花了半天时间在三四个文件里搞了一个新功能中途顺手改了一个bug、优化了一段日志打印。现在要提交怎么办用git add .会把所有改动混在一起用git add 文件名也只能按文件粒度控制。真正优雅的方式是用git add -p进入交互式暂存界面Git会把每个文件的改动拆成一个个代码块hunk你逐个决定暂存还是不暂存甚至可以按s把一个代码块再拆细。git add -p src/App.js执行后Git会显示第一个代码块的diff然后等着你输入指令y暂存这个块n不暂存s拆分e手动编辑。我在实际操作中e和s用得最多尤其是当一个函数里既有新功能逻辑又有bug修复时用s拆成两个提交是最理想的处理。如果只想暂存指定文件不要用git add .推荐用git add src/App.js src/utils/api.js这种显式列表或者直接交互式选择git add -i对于已经暂存但突然发现某个文件不该提交的情况用git restore --staged 文件名把它从暂存区移回工作区这个命令等价于老版本Git的git reset HEAD 文件名注意它不会删除文件本身的改动。我从SVN时代转过来时最大的不适应就是提交前还要先add这件事。SVN的提交是一步到位的改完文件直接svn commit。而Git刻意把选择内容和提交内容分成两步目的就是让你在提交前有机会思考这次提交到底应该包含什么、不应该包含什么。这是Git在工程理念上远超SVN的设计之一很多人却把这一步浪费掉了养成git add .到手就提交的坏习惯。4. git commit指令与参数的高频实战用法4.1 基础命令与参数从 -m 到 --amend 的完整梳理先回顾一下提交的基础命令。最常用的就是git commit -m feat: 添加用户注册接口一条命令完成提交并填入标题。多行提交信息带正文可以用多个-mgit commit -m fix(auth): 修复Token过期后接口返回401的问题 -m 原因是请求拦截器没有统一处理401响应现在增加了刷新Token后重放请求的逻辑。git commit -a这个参数我特别提一下它会自动把所有已跟踪文件的改动暂存并提交相当于git add -u git commit。听起来很方便但对新手其实是个陷阱——它会把你工作区里所有已跟踪文件的修改全部打进这次提交完全绕开了选择内容这步设计。我的建议是老老实实先git add再git commit不要偷懒用-a。git commit --amend是我几乎每天都会用到的参数它的作用是修改上一次提交。有两种典型场景一是刚提交完发现漏了一个文件二是提交完发现Commit Message打错字了。操作方式是# 场景一补充漏提的文件 git add 忘记添加的文件.js git commit --amend --no-edit # 不加 --no-edit 会打开编辑器让你重新填写提交信息 # 场景二修改提交信息 git commit --amend -m fix: 正确的提交描述--amend的本质不是修改原提交而是创建一个全新的提交替换掉它所以旧的提交会从当前分支历史中消失。这引出一个最重要的使用禁忌只允许amend那些还没有推送到远程的本地提交。如果有人已经基于旧提交做了分支开发你amend之后再强制推送对方的提交历史和你的就对不上了。在VS Code里操作Git也很顺手源码管理面板会清晰显示变更文件列表勾选需要提交的文件在上方输入框写提交信息CtrlEnter就能提交。对于习惯Git Bash却不是命令行高手的新人VS Code这种图形化的方式降低了不少门槛。4.2 提交后反悔怎么办reset、revert和reflog的救援组合提交这件事做得再规范也难免有反悔的时候。提交完发现有大问题想要撤销很多人第一反应是git reset --hard但这是最危险的命令之一搞不好会让你丢掉一整天的工作成果。理解三个命令的区别比死记参数更重要。git reset是回退分支指针有三个模式--soft只回退commit保留暂存区和工作区--mixed默认回退commit和暂存区保留工作区--hard三者全部回退工作区的文件改动直接丢弃。如果只是想撤销上一次提交但保留代码改动用git reset --soft HEAD~1就够了想连暂存区一起清理用默认的git reset HEAD~1。git revert则是反向提交它会生成一个新提交来抵消目标提交的改动不会改写历史适合用来撤销已经推送到远程的公共提交。团队协作中如果发现某个提交有严重问题正确操作是git revert commit-hash而不是git reset因为后者要求强制推送会让其他成员的本地分支处于不一致状态。git reflog是最后的救命稻草。它记录了你在本地仓库所有的HEAD移动历史包括reset、amend、rebase等操作。假设你误执行了git reset --hard HEAD~3发现代码不翼而飞只要执行git reflog找到回退前的commit hash再git reset --hard 那个hash就能恢复如初。团队里如果有人跑来跟我说我的代码丢了git push都救不回来我第一句话永远是先看看reflog八成能找回来。4.3 Git Hooks与提交前自动化让机器替你查岗提交信息规范和原子性提交可以靠自觉但我更推荐用Git Hooks把它变成强制标准。Git在.git/hooks/目录下提供了一系列钩子脚本其中和提交最相关的是pre-commit和commit-msg。前者在提交前运行可以执行代码检查后者在提交信息填写完毕后运行可以校验信息格式。一个最简单的pre-commit脚本如下它会在每次提交前跑指定目录下的测试和lint#!/bin/sh echo Running pre-commit checks... npx eslint src/ npm run test如果ESLint检查失败脚本会以非零状态退出提交自动被拒绝。commit-msg脚本则用来校验格式比如用正则检查提交信息是否匹配Conventional Commits规范#!/bin/sh message$(cat $1) if ! echo $message | grep -qE ^(feat|fix|docs|style|refactor|perf|test|chore|build|revert)(\(.\))?: .{1,50}$; then echo 提交信息不符合规范请使用 Conventional Commits 格式 exit 1 fi现代前端项目里更多人会借助Husky工具管理Git Hooks配合lint-staged实现只检查本次暂存的代码。比如在package.json里配置{ husky: { hooks: { pre-commit: lint-staged, commit-msg: commitlint -e $HUSKY_GIT_PARAMS } }, lint-staged: { *.{js,ts,vue}: [eslint --fix, git add] } }这套方案的最高价值在于把质量门禁前移到了每一次提交之前而不是等到CI阶段才报错。CI报错还能勉强补救但提交信息不合规范、代码被ESLint一堆警告污染这类问题在源头拦截下来是最省事的。5. 提交之外的协作规则分支策略与提交后协作5.1 分支命名与工作流一个适度够用的协作框架提交完了还要推得出去、合得进来这依赖团队的分支策略。业界有非常复杂的Git Flow分支多到新手头皮发麻master、develop、feature、release、hotfix层层嵌套。有些团队确实需要这种严格流程但对多数中小型项目和快速迭代的互联网团队我认为过度设计反而拖慢节奏。我更推荐基于主干开发的简化模型main或master始终保持可发布状态新功能在短命的分支上开发完成后通过Merge Request合回主干。无论采用哪种模型分支命名都需要规范。我的习惯是类型/描述的结构feature/用户注册、fix/登录超时、hotfix/支付成功回调错误、chore/升级依赖。这里的描述建议用简短的中文或英文能让人不看代码就能猜出分支用途。分支名直接和提交标题一样也是项目历史的一部分。我在团队里见过一个让人抓狂的命名乱象有人用test、dev、123、my-branch这种谁都看不懂的名字分支多的时候根本不知道谁在干什么合并请求的上下文只能靠猜。定一个简单的命名规范配合提交信息规范整个项目的可读性会上一个台阶。5.2 Rebase还是Merge提交历史是讲给未来同事的故事这是Git社区争论最多的话题之一。merge会保留完整的实际开发轨迹分支上每次提交都记录在案合并时新增一个merge commitrebase则会把当前分支的提交搬到目标分支最新提交之上重写提交顺序让历史看起来像一条直线。# 合并方式一merge git checkout main git merge feature/user-register # 生成了一个merge commit历史保留两条分叉的交汇点 # 合并方式二rebase git checkout feature/user-register git rebase main # 将feature分支的提交重放到main最新提交之后历史呈线性我的观点是使用rebase保持本地开发分支的线性历史使用merge合入公共主干分支。具体来说自己开发分支在合并到main之前先git rebase main把主干上的新提交吸收进来解决完冲突后用git merge --no-ff合回主干。这样做的好处是本地提交顺序整齐定位问题方便而main分支上的merge commit记录了明确的关口将来发布时可以快速回看某个版本包含了哪些完整功能。注意这条规则有个铁律不要再重新rebase已经推送到远程且被多人使用的分支否则会改写公共历史导致其他人的本地仓库出现一大片冲突这是团队协作里最严重的错误之一。5.3 解决冲突的正确姿势少冲突靠勤快真冲突靠流程合并冲突是多人协作绕不开的话题。冲突的本质是两个分支修改了同一处代码而且Git无法自动判断该保留哪个版本。减少冲突最有效的手段不是解决得快而是提交得勤、拉取得勤。在一个功能分支上持续干活超过三天还不同步主干的进度等合并时大概率积累一堆冲突如果每天甚至每半天就rebase一次主干冲突会少很多而且就算有也容易处理因为你的改动量小上下文也还记得清楚。真遇到冲突时Git会在冲突文件里插入标记 HEAD 当前分支的内容 被合并分支的内容 feature/xxx解决冲突的流程是打开文件看清楚和之间的差异手动决定保留哪边、两边都留还是重新改写删除标记行后再git add该文件最后git commit完成合并。这里有个从SVN时代带过来的旧习惯值得纠正SVN的冲突解决是我一改别人就等着而Git的冲突解决是在本地完成你没有理由慌张也没有理由觉得解决了冲突就万事大吉——提交后一定要把测试跑一遍因为手工合并很容易引入逻辑错误。6. 提交前的自查清单与高频问题排查速查6.1 提交前五分钟自查清单这些年在团队里推行Git规范我慢慢总结出一份提交前自查清单每次准备git commit前按顺序过一遍能挡掉90%的低级错误第一跑一遍git status看当前工作区状态确认没有遗漏的改动文件也确认没有把开发时的调试代码、日志打印、数据库连接串等敏感信息带进去。git diff再快速扫一眼具体改动有些问题不看diff根本发现不了比如误改了几行无关代码、把测试环境的API地址写死进代码里等。第二明确本次提交的一件事是什么确认暂存区里的文件改动和这件事相关。不相关的改动请git restore --staged移出去或者干脆先存放起来用git stash把无关改动搁置。第三运行相关的测试和代码检查。不用全量跑但至少把当前模块的单元测试、集成测试跑一遍确保绿色通过。项目配置了lint的也顺手跑一下避免把ESLint报错提交上去污染代码库。第四检查提交信息是否符合Conventional Commits格式标题是否能概括这批改动正文是否写清了为什么。确认没有把密钥、生产环境地址等敏感信息写进提交信息里。第五如果这是修复线上Bug的提交提交流程走完后第一时间在Commit Message的footer里关联Issue编号方便团队在问题追踪系统里回溯。6.2 提交过程高频问题排查速查表按项目经验整理了一份提交相关问题的速查表遇到问题直接对号入座现象可能原因解决方案git commit报错说缺少用户名邮箱未配置user.name / user.email全局或本地配置后重新提交提交进去的内容少了几个文件使用了git add 某文件后漏了其他文件git add补齐后git commit --amend --no-edit补充进上一次提交提交信息写错了-git commit --amend -m 正确信息重写提交信息把不该提交的文件提交了.gitignore配置不全或操作疏忽若未推送git reset --soft HEAD~1后从暂存区移除文件并重新提交若已推送用git rm --cached删除跟踪后提交提交后想撤销改动提交内容有问题本地提交用git reset --soft HEAD~1远端公共提交用git revert hash误执行git reset --hard丢了代码-git reflog找回原commit hashgit reset --hard hash恢复合并时冲突太多分支太旧、同步太少先git rebase main解决冲突再合入主干以后勤拉取Windows上文件全部显示为已修改换行符配置不一致在.gitattributes中统一设置eollf让Git处理转换中文文件名显示为转义序列core.quotepath默认truegit config --global core.quotepath false提交后CI失败提交前没跑测试、lint运行 test/lint 通过后用--amend或新提交修复这份表里有一个我从踩坑中总结出的重要心得提交的时机宁可早一点、频率宁可高一点。本地开发时每完成一个逻辑闭环就提交一次比攒到晚上一次性提交稳得多。这样就算某次改动出了岔子你也只需要处理几十分钟内的代码而不是面对一整天的混乱。我个人在实际操作中的体会是Git本身只是一堆命令但用好它的关键是建立一种提交是给别人看的的心态。你的每一次提交都是在给未来的自己、未来的同事、未来的维护者写便条。写的时候多花一分钟把信息说清楚将来排查问题时可能就省下一个下午。这套实践我从团队新人培训一路带到长期项目维护越用越觉得当初花时间建立这些习惯是值得的。