直接开始。这篇是《Git入门指南》系列的第二篇上一篇咱们把安装、配置、SSH 这些地基打好了这一篇就进入正题日常用 Git 干活最频繁的那批基本操作。从 git init 到 commit从 diff 到 log从分支到标签再到远程仓库的协作我把这一年多在项目里真正摸过的命令、踩过的坑、总结出的习惯一次讲清楚。如果你刚接触 Git只想知道“每天要怎么用它”那这篇正好合适。如果你已经会 clone、commit但一直搞不懂工作区、暂存区到底怎么回事或者老是卡在合并冲突上这篇也能帮你把逻辑理顺。1. 环境准备安装 Git 前先想清楚这三点我知道很多人是直接一把梭把 Git 装上但用了一两个月之后回过头来发现版本选错、换行符没配、终端用得不顺手各种小毛病全冒出来了。所以这里先把环境准备说透。1.1 版本选择为什么没必要追新Git 官方版本更新节奏不快但也不慢。很多人一看有新版本就冲上去升级结果公司内网服务器比较旧或者团队成员用的版本太低互相之间交换仓库没出问题还好一旦出了兼容性提示就很烦。我的建议是选一个稳定版本比如当前主流的 2.30 到 2.40 区间不要追求最新更不要用老古董版本。具体到 Windows直接去 Git 官网下载安装包macOS 上如果装了 Homebrew用 brew install git 就完事Linux 就看你发行版对应的包管理器。这里有个细节值得提一下Windows 用户安装时一定会遇到一个选项调整 PATH 环境变量。默认选项是Git from the command line and also from 3rd-party software这个就用默认的不要改成 Use Git and optional Unix tools from the Command Prompt否则会把 Windows 自带的 sort、find 这些命令覆盖掉后面在脚本里容易出问题。1.2 换行符配置最容易被忽略的坑Git 官方在这点上做了妥协Windows 用 CRLF回车换行Linux 和 macOS 用 LF换行。跨平台协作时如果不处理你 pull 下来的代码可能每一行都被标成改动过diff 看起来像整个文件都变了。安装时那个Checkout Windows-style, commit Unix-style line endings选项意思是在工作区转成 CRLF提交到仓库时转成 LF。听起来很智能但实际协作中我踩过不少坑如果是纯 Windows 团队项目问题不大一旦有 Linux 或 macOS 同学加入就可能出现“我明明没改这行Git 却总提示冲突”的诡异现象。所以我的做法是团队统一在仓库根目录加一个.gitattributes文件明确指定文本文件的换行符处理方式* textauto *.js text eollf *.ts text eollf *.json text eollf *.md text eollf这个文件提交到仓库后所有成员不管用什么系统Git 都会按规则处理换行符。比依赖每个人的全局配置靠谱得多。1.3 验证安装是否成功装完别急着用先跑一下git --version能看到版本号说明基本环境没问题。再跑一句git config --global user.name git config --global user.email这两条输出如果不为空说明身份信息也配好了。如果为空先补上因为 Git 每次提交都会记录这两个字段没配齐会让你提交时提示Please tell me who you are。很多新手第一次提交就被这个吓住其实解决方式很简单git config --global user.name your name git config --global user.email youexample.com2. 初始化到第一次提交建立版本管理的最小闭环这一步相当于给项目装上一个“存档系统”。我先直接给出一套最常用的操作路径然后在下面仔细拆解每一条命令背后的作用和常见误区。2.1 两种开始方式init 和 clone如果你是从零开始一个新项目就在项目根目录执行git init执行完会生成一个隐藏的.git目录整个项目的版本信息都存在这里面。注意这个目录说白了就是 Git 的“记忆库”它里面的东西不适合直接手改哪怕是误删一个文件都可能让整个历史记录坏掉。如果是从远程仓库开始协作那就不是 init 了而是 clonegit clone https://github.com/user/repo.git或者用 SSH 方式git clone gitgithub.com:user/repo.git我建议有条件用 SSH 就用 SSH。原因很简单HTTPS 方式每次 push 都要输账号密码虽然可以配置缓存但总归多一步操作。SSH 配置好密钥之后push/pull 都不需要再输密码效率高很多。2.2 第一次提交的标准五连初始化仓库之后第一次提交建议按这个顺序来git status git add . git commit -m feat: 项目初始化 git log很多人一上来就 add 然后 commit中间跳过了 status 和 log这其实不是不行但对新手来说很容易出现“稀里糊涂提交了一堆不该提交的文件”的问题。git status是看一眼当前仓库的状态哪些文件是新增的、哪些被修改过、哪些已经在暂存区。git add .是把当前目录下的所有改动加入暂存区。git commit才是真正把暂存区的内容固化成一次版本记录。git log用来查看提交历史确认这次提交是否成功。我在第一次提交这件事上吃过的亏是没有先写好.gitignore就把依赖目录和编译产物全提交上去了。比如 Node 项目的node_modulesJava 项目的targetPython 项目的__pycache__。这些目录动辄上千个文件一旦进去版本库不只是仓库体积爆增后面每次拉代码都会觉得卡。更麻烦的是这些文件会污染每次 diff 的视野。所以正确的第一次提交顺序应该是先写.gitignore再执行上面的五连。2.3 commit 信息怎么写才不后悔我之前见过很多提交信息是 “update”、“fix”、“修改”甚至还有 “123”。这种信息在写的时候特别爽但三个月后再看根本不知道那一次提交到底做了什么想用git bisect定位问题都无从下手。一个实用的格式是类型: 简要说明。比如feat: 新增用户登录接口fix: 修复移动端弹窗点击穿透问题docs: 更新 README 部署说明refactor: 重构订单状态机逻辑原因很简单提交信息本质上是写给未来的自己和其他协作者看的。Git 不要求信息格式但一份好的提交信息能让团队排查问题省下一大半时间。我自己在用这种格式后最直观的变化是回滚版本时靠git log --oneline就能很快锁定目标提交。3. 深入理解 Git 的暂存区为什么不是直接 commit 文件这个部分我要花点篇幅专门讲因为很多人用 Git 用了很久仍然没弄明白暂存区存在的意义。3.1 工作区、暂存区、版本库的关系你可以把 Git 仓库想象成一个三层的抽屉最表层是你正在编辑的文件也就是工作区。中间一层是暂存区类似一个“待打包区”你想把哪些文件放进下一次提交就先git add把它们挪到这一层。最底层是版本库也就是历史记录。只有git commit暂存区里的内容才会真正形成一个不可变的版本。为什么要多这一层暂存区直接用“保存”不好吗实际开发中一个功能可能涉及十几个文件的改动但其中两个文件可能只是为了调试加的临时日志不适合提交。有了暂存区你就可以一个一个地把真正要提交的文件git add把不该提交的排除在外。这样提交的每一个版本都是“干净”的不会夹带调试代码。3.2 最常用的 add 姿势git add有好几种用法我按使用频率排个序git add specific-file.js只暂存某一个文件最精准。git add src/暂存某个目录下的所有改动。git add -p这个我强烈推荐它是一个交互式命令会逐块告诉你哪些地方有改动让你决定是y暂存这一块、n跳过、还是s再拆细一点。当你在同一个文件里既有正式改动又有调试代码时-p就是救命的。git add .虽然简单粗暴但前提是你对当前目录下的改动有足够的判断。我见过有人在 IDE 里无意改了一个配置文件顺手git add .全提交了结果导致别人的环境变量被覆盖。这种问题排查起来特别费劲因为根本不是代码逻辑问题。3.3 撤销后悔操作add 错了怎么办如果git add加错了文件不要慌git reset HEAD file-name这条命令会把文件从暂存区移回工作区但不会改动文件本身的修改内容。也就是说你的改动还在只是不再处于“待提交”状态。如果连文件本身的修改也想放弃那就用git restore file-name或者更传统一点的写法git checkout -- file-name我特别提醒一句git restore是直接用版本库里的内容覆盖当前文件你对这个文件做的所有未提交修改都会消失而且不可恢复。所以执行这种命令前最好确认自己真的不需要那部分改动了。4. 查看变更与历史diff 和 log 的正确打开方式提交之前先看看自己改了什么这是 Git 使用里最值得养成的习惯。4.1 三种查看 diff 的层次git diff是我每天用最多的一条命令它分三个层次git diff查看工作区相对暂存区的差异。说白点就是“我改了哪些还没 add 的”。git diff --cached查看暂存区相对已提交版本的差异。就是“我 add 了哪些还没 commit 的”。git diff HEAD把工作区全部未提交的改动都列出来包括已暂存和未暂存的。多数情况下我习惯在 commit 之前跑一遍git diff --cached确认即将提交的内容确实是我想提交的。这个习惯帮我避免过好多次误提交比如把写错的接口地址、遗留的 console 日志、临时的测试配置都挡在了版本库外面。4.2 让 log 输出更有可读性默认的git log输出太冗长一屏看不了几条。我平时更常用这些方式git log --oneline每条提交只显示一行简洁到一眼看十条以上。git log --graph --oneline --all以图形方式展示分支结构适合看分支合并情况。git log -3只看最近三条提交适合快速确认刚才操作的结果。还有一个特别实用的参数是--author按提交者过滤git log --authorxxx当你想复盘某个人在某个时间段里到底提交了什么这条命令很有用。4.3 提交信息写错了怎么办每次git commit之后如果发现注释写错了或者漏了一个文件不需要重新提交一次新 commit。可以用git commit --amend这条命令会把当前暂存区的内容和上一次提交合并成一条新的提交记录。换句话说它把上一次提交“顶掉”了。但我要强调一个使用场景限制如果这个提交已经被 push 到了远程分支而且有其他同事基于它拉过分支或者合过并那就不要再 amend 了。你已经推出去的历史就这样被改写同事那边会出现莫名其妙的冲突或者历史不一致。我在这上面吃过亏有一次改了已经 push 的提交再强推上去结果同事本地分支和远程分支的历史彻底错位最后大家花了大半天才把分支恢复成一致状态。5. 分支操作并行开发的基石分支是 Git 相对传统版本管理工具最核心的优势之一。它让你可以同时维护多套代码状态互相不打扰。5.1 分支的日常用法查看当前分支git branch创建并切换一条新分支最简写法是git checkout -b feature-login等价于两条命令git branch feature-login git checkout feature-login新版 Git 也支持用git switch来切换分支git switch -c feature-login我个人还是习惯checkout -b因为这套写法兼容性最好不管在哪台机器上都不会依赖 Git 版本。切到新分支后你在这个分支上的所有提交都只属于它自己不影响主分支。等开发测试完成再合并回主分支。5.2 合并分支的两种方式merge 和 rebase合并分支时git merge是最直接的方式git checkout main git merge feature-loginmerge 会把两个分支的开发历史合并产生一个新的“合并提交”整个过程像是两条路交汇到一个点。而git rebase则是把你当前分支的提交“摘下来”重新接到另一个分支的最新节点后面。效果从结果上看历史会是一条直线看起来更干净。但代价是它改写了提交顺序如果处理不当同样会影响其他协作者。我给初学者的建议是先老老实实学会用 merge。因为 merge 的语义最简单谁和谁合并产生一个节点清清楚楚。rebase 适合你已经对 Git 历史非常敏感、知道自己在干什么的情况。5.3 合并冲突遇到别慌冲突就是两个分支改动了同一个文件的同一片区域Git 不知道该听谁的。此时工作区文件里会出现这样的标志 HEAD 你当前分支的代码 正在合并进来的分支的代码 feature-login你需要手工把这段内容整理成正确的代码删掉三对有争议的标记行然后git add file-name git commit注意合并冲突并不代表代码被搞坏了而是 Git 把裁决的权利交给了你。处理完冲突后最好重新跑一遍测试尤其是涉及同一函数签名、同一配置项的情况很容易出现“合并时没冲突运行起来全是错”的隐藏问题。6. 远程仓库协作push、pull 和 fetch 怎么选本地操作熟练之后就一定要接触远程仓库了。现在大多数团队用 GitHub、GitLab 或者 Gitea 这类平台来托管仓库。6.1 关联远程仓库如果你的项目是从git clone开始的那远程地址已经自动配好了。但如果你是自己git init之后想关联远程就需要手动加git remote add origin gitgithub.com:user/repo.git这样之后的 push 和 pull 就可以默认走origin这个远程仓库了。要查看当前关联了哪些远程地址git remote -v注意-v是大小写敏感的写成git remote -V反而不会输出期望的信息。6.2 push 的常见姿势git push origin main把本地 main 分支推送到远程 origin。如果是第一次推送新分支而且远程还没有对应分支需要加上-u参数git push -u origin feature-login-u的含义是“设置上游分支”设置了之后后续在这个分支上直接执行git push就能自动找到对应远程分支不用每次都输入 origin 和分支名。每次 push 之前我习惯先 pull 一下最新的远程代码而不是等 pusl 被拒绝再补救git pull origin main如果你的本地分支和远程分支历史有分叉pull 可能会自动合并也可能直接报冲突。遇到冲突的处理方式跟分支合并时一样解决冲突然后提交。6.3 pull 到底是 fetch 加 merge很多人用 Git 很久却不清楚 pull 和 fetch 的区别。git fetch是把远程仓库的最新提交下载到本地但是不会动你当前分支的任何东西。git pull则是在 fetch 之后再把远程的最新内容合并到当前分支。所以如果你想先看看远程改了什么不影响自己的工作就先用 fetchgit fetch origin git log origin/main --oneline等确认远程确实有新的提交再决定要不要 merge 或者 pull。这个习惯在团队协作中特别好用尤其是远程存在多条分支、你又不确定别人有没有推过改动的情况下。我刚开始用 Git 时特别不喜欢 fetch 这个多出来的步骤总觉得 pull 一步到位更快。但后来有一次局部代码没提交完pull 直接把远程的改动合并进来了工作区的结构被弄乱跟本地的半成品状态混在一起处理起来相当狼狈。从那以后我在本地还有未提交改动的时候一定会先 fetch 看一眼再决定怎么合。7. 标签、储藏与忽略文件三个高频实用点这三个功能不算是起步就必须掌握的内容但在真实开发中用到的频率已经高到值得专门讲一讲。7.1 标签给版本打上标记打标签通常用在发版本的时候。比如代码测试通过要发一个 v1.0.0git tag v1.0.0标签和分支不一样它指向某个固定的提交不会再移动。以后不管代码怎么迭代都能通过这个标签快速回到这个版本。查看所有标签git tag -l删除本地标签git tag -d v1.0.0推送标签到远程git push origin v1.0.0如果想一次把本地所有标签推到远程git push --tags标签的实战价值最大的是发布程序。我们之前上线过一个功能部署系统就是靠 Git 标签来构建指定版本的产物。开发分支天天变但只有带v前缀的标签才允许走发布流程这样运维那边也能很清爽地定位到到底构建的是哪一份代码。7.2 储藏临时切换不再慌乱git stash是一个被低估的命令。当你正在 feature-A 分支开发一个功能改了一半突然被告知需要马上切到 main 修个紧急 bug但当前这半截改动又不想提交怎么办git stash这一条命令就能把你当前未提交的改动都“储藏”起来工作区恢复到干净状态。然后你可以放心地切走。等修完 bug再切回来执行git stash pop改动就又会恢复到原来的状态。查看已有的储藏列表用git stash list如果储藏了多个内容可以用git stash pop stash{1}指定恢复某一个。我有一个额外建议stash 了之后最好尽快 pop不要在列表里积压太多。因为 stash 的内容是按日期和顺序记录的如果你一次囤了五六个过两周再来看自己都想不起来每一个里面都有啥了。7.3 .gitignore别让无关文件污染仓库前面提到过.gitignore的重要性这里再系统地说一下。它就是一个纯文本文件每一行写一条忽略规则支持*通配符。一个典型的 Node 项目可以这样写node_modules/ dist/ .env *.log .DS_Storenode_modules/表示整个目录都忽略.env表示环境变量文件不提交*.log表示所有日志文件都不提交。要注意的是.gitignore只能影响“还没被 Git 跟踪的文件”。如果某个文件已经被提交过再往.gitignore里加它是不生效的。这时候需要先把文件从版本库移除git rm --cached .env--cached参数表示只从暂存区移除不删实际文件这样之后这个文件就不会再被 Git 跟踪了。8. 常见问题排查实录这部分是我从真实工作里收集到的高频错误和信息整理成一份速查式的列表方便你遇到问题直接对照着处理。8.1 “git 不是内部或外部命令”这个报错最常出现在刚装完 Git 的 Windows 上。原因就是安装时没有把 Git 加到 PATH或者安装后没有重启终端。解决办法重新运行 Git 安装包在调整 PATH 那一步选Git from the command line and also from 3rd-party software装完重启命令行窗口即可。8.2 “Please tell me who you are”这个在 2.2 小节里提过是因为没有配置用户名和邮箱。执行git config --global user.name your name git config --global user.email youexample.com一旦配好它会影响所有仓库所以注意用你自己的名字和常用邮箱。如果你在一个项目里要用不同身份可以在项目目录下不加--global覆盖配置。8.3 “does not appear to be a git repository”你可能在一个不是 Git 仓库的目录里执行了 Git 命令。先用ls -a看看目录下有没有.git文件夹或者直接git init把它变成仓库。更常见的情况是你人还在子目录里但子目录其实不属于任何 Git 仓库的范围内。先cd到仓库根目录再执行命令。8.4 push 被拒绝报错信息通常长这样! [rejected] main - main (fetch first)意思是远程有本地没有的提交。解决办法很简单先 pull 或 fetch merge再 push。如果 pull 后依旧出现大量冲突说明两边改动范围比较大这时要先静下心来一个一个解决冲突文件不要试图用git push --force绕过因为没有合理的理由强推其实是危险动作会覆盖远程提交影响团队其他人。8.5 误删或误改代码想找回如果改动还在尚未提交的状态能用git restore找回的部分有限只有那些你曾经提交过的内容才被 Git 记住。找回误删但尚未 commit 的文件的“上一次提交版本”git checkout -- file-name找回误删且已经 commit 过的历史版本可以用git log找到那个提交的哈希值然后git checkout commit-hash -- file-name所以我总跟团队说重要的阶段性成果抓紧提交一次不一定要是一条完整的 feature哪怕只是“今天早上调通了数据处理逻辑”也可以提交。Git 最大的安全感恰恰来自一次次及时的提交。9. 一套我认为合理的基本操作流程前面讲了这么多零散的命令最后我结合自己日常开发的节奏把从开始一个功能到合入主干的流程整理成一套模板你可以直接照着用。第一步在 main 分支上更新到最新git checkout main git pull origin main第二步创建功能分支git checkout -b feature-xxx第三步在功能分支上开发和提交git add relevant-file.js git commit -m feat: 实现 xxx每完成一个有逻辑意义的阶段就提交一次不要等到最后再一个巨大的提交。第四步把 main 的新改动同步到当前分支可选但推荐git fetch origin git merge origin/main第五步处理冲突、测试通过之后推送到远程git push -u origin feature-xxx第六步在远程平台发起合并请求Pull Request 或 Merge Request等其他人评审通过后合入 main。这套流程的核心是功能分支隔离开发主干保持稳定每一个提交有清晰目的。坚持下来不但自己代码管理得清爽团队协作成本也会下降一大截。我在实际工作中最大的体会是Git 的命令本身并不多难的是你对“每个操作会对历史记录产生什么影响”有清晰的认识。很多时候一个命令的误用短期看不出问题但一周、一个月后当你需要在复杂历史里定位问题时那些当初偷的懒都会加倍还回来。认真理解暂存区的作用规范提交信息谨慎对待强推和 amend这些习惯比背全命令列表有价值得多。最后再分享一个小技巧如果某天你用 Git 操作时心里发虚先复制当前目录做个备份再去执行。这不是胆小而是资深用户都在用的“安全网”。等你在一个有惊无险的误操作里成功把仓库救回来你对 Git 的理解会一下子突飞猛进。