git常用命令,这四个字看起来简单,但真要把它们用得顺手,背后要理解的东西比单纯背命令多得多。我前前后后用Git差不多十年,带过不少新人,也亲眼见过有人一上来就到处复制命令,结果把整个开发分支搞乱、连同行代码都跟着遭殃的场面。这篇内容不整那些花架子,就从日常开发的真实需求出发,把最高频的Git命令按"原理→实操→避坑"这条路子拆开讲。适用面很宽,前端、后端、客户端、数据分析都能直接参考,新手可以系统入门,老手也能对照检查自己的操作习惯。
1. 玩转Git前必须搞懂的三件事
1.1 版本库、暂存区和工作区到底在干嘛
很多人学Git最大的障碍,不是命令记不住,而是不知道每条命令到底在操作哪一块地方。Git把一次提交拆成了三个区域:工作区就是你肉眼能看到的文件夹,代码在里面随便改;暂存区可以理解成一个购物车,git add 就是往购物车里放东西;版本库才是Git真正存数据的地方,git commit 就是把购物车里的东西结算入库。搞清楚这个链路,很多命令的用途就自动对上了。
仓库目录下那个.git文件夹,就是版本库的实体。里面有个objects目录专门存放每次提交产生的数据对象,有refs目录存各种分支和标签的引用信息,还有一个HEAD文件记录你当前在哪个分支。这个目录千万不要手痒去删,删了等于把整个仓库的历史全格式化掉。日常工作流的本质,就是工作区不断产生修改,修改经过暂存区,最后落进版本库形成历史记录。
1.2 分支的本质只是指针
分支这个概念,是我见过最多人误解的地方。不少刚开始用Git的人,以为创建分支就是复制了一套代码出来,担心文件变多、磁盘占满。实际上,Git里的分支本质上只是一个指针,它指向某一条提交记录。创建新分支只是在refs/heads下面新建一个引用文件,不复制任何文件内容,所以创建分支是瞬间完成的操作。
真正记录东西的是commit对象,它里面保存了当前快照、父提交、作者和提交信息。分支指针像贴纸一样贴在某条提交上,随着新提交不断往后挪。HEAD则是另一个指针,决定你当前在哪条分支上,它指向的是分支名,而不是直接指向提交。理解这层关系之后,git switch和git checkout为什么要你先把工作区收拾干净,就很容易想明白了——切换指针的时候,工作区文件也要跟着换成另一棵树的模样。
1.3 Git命令的主脉络:一条主线和四条辅助线
Git命令看着多,其实可以梳理成一条主线和四条辅助线。主线是从本地到远程的完整提交链路:git add把改动放进暂存区,git commit把暂存区固化成提交,git push把本地提交推送到远程,git pull把远程更新拉到本地。市面上百分之八十的操作,都发生在这条链路上。
另外四条辅助线分别是查看、撤销、远程管理和配置。查看用的命令是git status、git diff、git log;撤销用git reset、git revert、git restore;远程管理用git remote、git fetch;配置用git config。你先把这几个大方向记在脑子里,再往里面填具体命令,就不会像无头苍蝇一样乱撞。我教新人的时候从来不要求背命令,只要求先说出"我现在要干什么,这个动作发生在哪个区",想清楚了,命令自然就出来了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从零到一次提交:日常最高频命令拆解
2.1 初始化仓库与克隆已有项目:两种打开方式
接触一个项目通常有两条路。第一条是从零开始,在空目录里执行git init,把当前目录变成一个仓库,然后开始写代码。第二条是加入一个已有的项目,用git clone <远程地址> <本地目录名>把整个仓库连同历史记录拉到本地。git clone本质上做了三件事:下载仓库数据、创建远程跟踪引用、把你放在默认分支上。所以克隆完直接就能开始开发,连git remote add都可以省掉。
git init之后建议马上检查两件事。第一,确认当前分支名是你想要的,因为不同平台默认分支名可能是master也可能是main,如果团队约定用main,可以执行git branch -m main改过来。第二,立刻创建一个.gitignore文件,把node_modules、vendor、target这类依赖目录和编译产物挡在门外。我见过太多项目因为忘了这一手,第一次提交就把上千个依赖文件全塞进仓库,之后每次拉代码都痛苦无比。
2.2 用状态命令精准掌握仓库动态
git status是使用频率最高的命令,没有之一。它能告诉你当前在哪个分支、暂存区有什么、工作区有哪些改动。我习惯用短格式git status -s,输出简洁清晰,每行一个文件,前面用字母标记状态:M表示已修改,A表示已暂存,??表示未跟踪的新文件。一眼扫过去,整个仓库的情况就全掌握了。
git diff用于查看具体改了什么内容。不带参数时,它比较的是工作区和暂存区之间的差异,也就是说,只显示"你还没add的那些改动"。加了--cached参数后,比较的则是暂存区和版本库之间的差异,也就是"你已经放进购物车、但还没提交的内容"。这个区别我反复强调:提交之前一定要先git diff --cached过一眼,确认要提交的确实是想要的改动。还有人会问我git diff <commitA> <commitB>怎么用,这个通常用于对比两个提交版本之间到底改了什么,做代码审查或者排查问题的时候非常常见。
2.3 暂存与提交:提交前的关键一步
git add是把工作区改动放进暂存区。常用姿势有三种:git add <文件名>精准添加某个文件;git add .把当前目录下所有未被忽略的改动全部加进去;git add -p进入交互模式,一个文件一个文件地确认要不要加,适合你自己都还没想清楚要不要一起提交的时候。第三种用得少,但真心推荐,能帮你养成精确提交的习惯。
git commit是把暂存区内容固化成一条提交记录。最简单的用法是git commit -m "提交说明"。如果想跳过暂存步骤,可以用git commit -am "提交说明",它会自动把已跟踪文件的修改暂存并提交。但是注意,这个-a对新增文件无效,新创建的文件该add还是得add。我现在个人项目上比较喜欢的流程是git add -p加上git commit,提交粒度小、信息准确,以后回滚排查也轻松。
2.4 提交信息怎么写才专业
提交信息看似是小事,实际上是对自己的代码历史负责。我见过一堆提交信息写着"update"、"修改"、"fix bug",遇到问题翻log时完全看不出这条提交改了什么,只能一个一个执行git show去看代码,效率极低。写提交信息有一个基本格式可以套用:开头用标签表明类型,紧接着用一句话说明改动内容,比如feat: 增加用户登录接口的单元测试、fix: 修复日期格式化在跨时区场景下的偏移、docs: 补充部署文档中的环境变量说明。
如果改动内容比较复杂,建议在提交信息里加一个简短正文,说明为什么这么改、影响范围是什么。Git提交信息支持多行,git commit -m加多个-m参数就行,每个-m会被当成一个段落。提交完成后,用git log --oneline查看提交历史,每行就是一条简洁的提交记录。配合--graph参数还能看到字符画风格的分支图,整体布局一目了然。
3. 分支管理:多人协作不掉链子的关键
3.1 创建、切换、删除分支的正确姿势
分支操作在实际开发中比大多数人想象得更高频。每接一个新需求,正规做法是从最新的主干上拉一条自己的开发分支,在上面开发,开发完再并回去。git branch <分支名>是创建分支,git switch <分支名>是切换分支,两个动作合并起来就是git switch -c <新分支名>。以前更流行的是git checkout -b,效果一样,但现在git switch语义更清晰,分支切换和文件还原的职责分得更开。
删除分支用git branch -d <分支名>,它有个保护机制:如果分支上有未合并的提交,会拒绝删除并给出提示。如果确实确认这些提交不要了,可以用大写-D强制删除。我给团队定了个小规矩:删除分支之前执行一次git branch -d看它让不让删,如果提示未合并,八成是这个分支上有还该保留的代码,别急着上-D。
3.2 合并与变基:两条整合历史的路线
把别人的改动合到自己分支上,通常有两条路。git merge <分支名>直接把两个分支的历史合并成一个新提交,历史会保留分叉的形状,git log --graph画出来能看到清晰的岔路。git rebase <分支名>则把当前分支的提交"摘下来",重新接到目标分支的最新提交后面,让历史变成一条直线。
两者选择上我的建议比较实用:需要往主干合并代码时,多用git merge --no-ff <分支名>,强制生成一个合并提交,能清楚看出这次合并引入了哪条功能分支;在自己的个人分支上开发时,想整理提交顺序,可以用git rebase <主干分支>往主干上对齐,让提交历史干净线型。但有一条强制红线:绝不rebase团队共享的分支。共享分支上的提交被改写,会让其他同事本地历史错乱,pull的时候会撞出一堆无法解释的冲突。
3.3 合并冲突的实战解决流程
冲突是Git最吓人的场面,但解冲突本身不难。当你执行git merge或git rebase时如果提示冲突,Git会在冲突文件里插入三段标记:<<<<<<< HEAD到=======之间是当前分支的内容,=======到>>>>>>>之间是对方分支带入的内容。你需要打开文件,找到这些标记,手动决定保留哪边、怎么拼接,最后把标记连同一方或两方的代码一起删除。
解完冲突之后流程有区别。git merge冲突时,保存文件后git add这个文件,再执行git commit即完成合并;git rebase冲突时,也是git add之后,但下一步执行的是git rebase --continue,让Git继续往后重放剩余的提交。如果发现自己越解越乱、想回到祖先状态,执行git merge --abort或git rebase --abort,它会干净利落地恢复到操作前的状态,这算是我教新人时反复强调的"后悔药"。
4. 出错之后别慌:回滚与历史恢复命令
4.1 从git log到git reflog:找回被丢弃的提交
git log能查看历史,但它只显示从当前提交往后能追溯到的记录。如果通过git reset等操作把分支指针往后拨了,原本指向的提交就不会出现在git log里,很多人以为它被删了,急得满头大汗。实际上Git默认不会马上清掉这些提交数据,这就是git reflog登场的时候。
git reflog记录的是HEAD指针的每一次移动历史,包括提交、切换分支、重置、变基。执行一下git reflog就能看到一串操作记录,例如类似2a3b4c5 HEAD@{0}: reset: moving to HEAD~2这样的输出。要找回被重置掉的提交,只需要记住它对应的哈希值,执行git switch -c <恢复分支名> <哈希>就能把它还原到一条新分支上。这条命令救过我很多次,也帮同事捞回过动辄几百行改动,真心强烈建议每个Git用户把git reflog从入门时就学会。
4.2 git reset与git revert:两种撤销的正确选择
撤销命令里最容易混淆的就是git reset和git revert。git reset是把分支指针往后移动,相当于"回到过去"。它有三种模式,区别在于移动指针时怎么处理暂存区和工作区。--soft只移动指针,暂存区和工作区的内容都保留,适合想重新整理提交的场景;--mixed是默认模式,会同时清空暂存区,改动回到工作区;--hard最狠,指针、暂存区、工作区全部回到目标状态,工作区里未提交的改动会直接丢失。--hard这个命令,我每次执行前都会强迫自己再去看一眼git status确认无遗漏。
git revert则不同,它不会改动原有历史,而是生成一条新提交,把某个提交的改动反着做一遍。这意味着revert之后历史是完整连续的,适合用来撤销已经推送到共享远程分支的提交。原因也很简单:如果对共享分支执行git reset改写历史,之后所有人再push都会被拒绝,还得强制推送,容易把团队其他成员的本地仓库搞乱。所以我的准则就一句:提交还没push出去,随便用reset整理;一旦push到共享分支,想撤销就用revert。
4.3 用git stash临时藏起手头工作
正在写代码的时候被叫去临时看一个问题,代码又改到一半,这时想切换分支会很尴尬,因为Git不允许带着未提交的修改切换分支,怕覆盖工作区。git stash就是为这种场景准备的。执行git stash会把工作区的改动暂存到一个栈里,工作区瞬间恢复干净。等处理完别的事情,再执行git stash pop把改动恢复出来。
更细致一点,可以用git stash save "描述信息"给暂存内容加备注,用git stash list查看栈里存了几份。如果你只想应用某一次暂存,用git stash pop stash@{2}这样的索引形式指定。有时候我在代码还没成型的时候会暂存一下过个周末,回来之后用git stash show -p看看里面改了什么,确认还想要才pop。还有个细节:如果恢复时跟当前工作区有冲突,pop会停下来让你手动解决,解决完处理方式和普通冲突没有区别。反正记住stash不是垃圾桶,它更像是你的临时保险柜。
5. 远程协作与仓库管理
5.1 配置远程仓库:origin是怎么来的
本地仓库要跟远程仓库关联,用的是git remote系列命令。大多数团队默认把主要的远程仓库命名为origin,git clone来的项目会自动带好这个远程引用,你直接用就行。如果是git init起来的仓库,需要手动执行git remote add origin <远程地址>把两者关联起来。查看当前配置用git remote -v,它会显示fetch和push两种地址,如果发现地址写错了,可以git remote set-url origin <新地址>修改,比删了重建干净得多。
我还习惯在重要节点执行一下git remote show origin,它会连到远程仓库查询并显示远程分支状态、本地分支与远程分支的对应关系,以及哪些本地分支领先或落后远端。整理仓库引用的时候很有用,能直观看出哪些远程分支已经被别人清理掉了,本地缓存还留着,需要执行git remote prune origin去同步。
5.2 push与pull的完整链路:理解fetch和merge
本地提交完成之后,推送命令是git push origin <分支名>。第一次推新分支时,加上-u参数能同时设置上游跟踪关系,之后你就可以直接敲git push和git pull,不用再带分支名。git push做的事情是把本地commit对象和依赖的数据传输到远程,并更新远程分支引用。推送被拒绝的时候,多半是远程分支上有了你没有的提交,这时直接git pull把远程更新拉下来,合并完再推。
这里要分清两个概念:git fetch只是把远程数据下载到本地,不改变你当前工作区;git pull等价于git fetch加git merge,一步到位把远程更新合并到当前分支。合并不了时会直接进入冲突处理流程。我的习惯是:对于不确定的远程环境,先git fetch观察一下git log origin/<分支名>的变化,搞清楚了再pull,这套做法在团队协作时能少踩很多坑。
5.3 远程操作高频报错与处理方法
远程操作最常碰到的报错是! [rejected] non-fast-forward,意思是远程分支有本地没有的提交,直接push会被拒。解决办法不是--force硬推,而是先把远程更新pull下来合并或rebase,处理完冲突再push,历史才是连贯的。合并方式我一般推荐git pull --rebase,让本地提交重新放到远程提交之后,history干净,后面合并主干时也少一处乱分叉。
需要删除远程分支时,用git push origin --delete <分支名>,那之后远程分支就没了。如果是权限问题导致push失败,常见原因有两种:一种是远程地址配置成了https但账号没有相应权限,检查一下远程地址是否需要换成SSH形式;另一种是SSH key没加到代码托管平台,检查本地公开密钥是否已上传。这时候用ssh -T跟托管平台做连通性测试是常见做法。
6. 高频命令速查表与我的避坑清单
6.1 一表搞定Git常用命令
| 场景 | 命令 | 说明 |
|---|---|---|
| 初始化仓库 | git init |
把当前目录变成Git仓库 |
| 克隆项目 | git clone <url> [目录名] |
拉取远程仓库的完整历史到本地 |
| 查看状态 | git status -s |
短格式展示工作区和暂存区状态 |
| 查看改动 | git diff |
查看工作区未暂存的内容差异 |
| 查看暂存内容 | git diff --cached |
查看已暂存但未提交的差异 |
| 暂存文件 | git add <路径> |
把指定改动放入暂存区 |
| 提交 | git commit -m "信息" |
把暂存内容固化成一个提交 |
| 查看历史 | git log --oneline --graph |
一行一条提交并显示分叉图 |
| 创建并切换分支 | git switch -c <新分支名> |
一条命令完成分支创建与切换 |
| 合并分支 | git merge --no-ff <分支名> |
强制生成合并提交并入目标分支 |
| 变基 | git rebase <分支名> |
把当前分支提交重放到目标分支之后 |
| 暂存改动 | git stash |
临时保存工作区改动 |
| 恢复暂存 | git stash pop |
恢复最近一次暂存并移除记录 |
| 重置回滚 | git reset --hard <commit> |
指针、暂存区、工作区全部回到指定提交 |
| 提交反向撤销 | git revert <commit> |
生成新提交反向修改,保留历史 |
| 查看操作日志 | git reflog |
列出HEAD指针的所有移动记录 |
| 配置远程 | git remote add origin <url> |
关联远程仓库 |
| 推送分支 | git push -u origin <分支名> |
首次推送并设置上游跟踪 |
| 拉取更新 | git pull --rebase |
拉取远程更新并把本地提交放到最新 |
| 删除远程分支 | git push origin --delete <分支名> |
远程分支移除 |
这张表基本覆盖了日常开发百分之九十五的场景,值得收藏起来当备忘录用。
6.2 操作习惯层面的避坑心得
篇幅最后,分享几个这些年攒下来的习惯。第一,提交之前一定先看git diff,哪怕只是顺手一瞟,也能拦下不少"把调试代码提交上去"的尴尬。第二,别习惯性地git add .,尤其在多任务切换的高压状态下,很容易把不该进同一个提交的文件混在一起,提交信息怎么写都不对,后续回滚更是麻烦。
第三,.gitignore要在一开始就配好,依赖目录、编译产物、本地配置文件和密钥文件必须提前排除。一些代码托管平台甚至允许上传公开密钥相关的配置,这类文件一旦提交上去,等于把敏感信息直接暴露给所有能访问仓库的人。第四,大文件别直接往仓库里推,往代码仓库塞超过几十兆的资源文件会拖垮所有人的克隆和拉取速度,该走对象存储走对象存储。
最后说一句,Git命令往上翻来翻去就那么多,真正能拉开差距的,是你遇到突发状况时知不知道有哪些兜底手段。git reflog和git stash这套组合,我到现在还在用。以前有次脑子一热对开发分支执行了git reset --hard,工作区里存着好几天的工作量瞬间消失,当场手就凉了,后来靠着git reflog把提交完整找了回来。打那以后我给自己定了条铁律:凡是可能破坏工作区内容的命令,执行前先备份好改动,即使备份只是多打一次git stash。这个习惯,值得每个Git用户从今天开始养成。
