Git三连操作全解析:add、commit、push从入门到实战
2026/9/19 9:39:05 网站建设 项目流程

1. 先搞懂Git的四个区:add、commit、push分别动了哪里

很多新手学Git,上来就敲命令,git add .一把梭,然后git commit -m "update",最后git push。运气好的时候一气呵成,运气不好就卡在各种报错里出不来。这背后的根源,是根本没搞清楚这三个命令到底在操作什么东西。

Git管理的代码,从你本地写出来到最终跑到远端仓库,要经过四个区域:工作区、暂存区、本地仓库、远程仓库。这四个区的名字听着抽象,其实用生活中的场景一类比就通了。

工作区就是你的项目文件夹,你日常写代码、改文件、删文件,全都在这里发生。这里的改动是"活"的,Git看得到,但还没有真正记录。暂存区像超市里的购物车,你从货架上(工作区)挑挑拣拣,把想买的(要提交的改动)放进车里。每次git add就是在往购物车里放东西。本地仓库像收银台打印出来的小票和后台的库存记录,git commit的那一刻,Git把暂存区里的内容拍了一张快照,永久记进本地版本历史。远程仓库则是公司仓库或GitHub、Gitee上的那份"公共账本",git push把你的本地记录同步过去,别人才能看到、拉取、协作。

理解这个流程后,很多困惑会迎刃而解:为什么我改了文件,git log看不到?因为你还没commit;为什么我commit了,同事还是看不到我的代码?因为你还没push;为什么我明明没改文件,git status却提示有改动?因为工作区和暂存区的内容不一致。

这一整套机制,解决的其实是"精确控制"的问题。如果每次提交都把所有文件一股脑塞进去,那就没法做到"这次提交只包含登录模块的改动,不包含我顺手改的配置文件"。Git用暂存区把"改了哪些"和"要提交哪些"拆开,让你能人为控制提交的粒度和边界。

实际工作中,我对新人的建议是:每敲一个命令前,心里默念一遍这个命令会把代码从哪个区挪到哪个区。add是工作区到暂存区,commit是暂存区到本地仓库,push是本地仓库到远程仓库。方向搞对了,错误率直接降一半。

2. add:怎么把想要的改动选进暂存区,而不是闭眼全选

2.1 基础用法:git add 的几种姿势

git add最常见的写法就几种,但每种的适用场景都不一样。

# 添加所有改动(包括新文件、修改、删除) git add . # 添加指定文件 git add src/main/java/com/example/UserController.java # 添加指定目录 git add src/main/java/com/example # 交互式添加,逐个确认每个代码块的去留 git add -p # 添加所有改动,包括已跟踪文件的删除 git add -A

git add .git add -A的区别,是很多老手都容易搞混的点。简单说,在Git 2.x版本里,两者行为几乎一致,都会把工作区里所有改动(包括新文件、修改、删除)加入暂存区。区别主要历史版本和子目录场景下,但现在日常使用,你基本可以认为它们等价。

2.2 为什么不建议无脑用 git add .

我知道,很多人包括我早期,都是git add .走天下。省事是真省事,但翻车也是真翻车。最典型的一次,我在项目里生成了本地的数据库配置文件,里面写着测试环境的连接密码。当时没来得及加进.gitignore,手一抖git add .,然后commit、push一条龙,密码就这么跟着代码上了远端仓库。虽然最后通过git rm --cached和改密码补救,但那一晚上的折腾,到现在想起来都肉疼。

所以我现在对团队新人的要求是:如果没有十足的把握,git add后面至少指定到目录层级。涉及配置文件、密钥文件、IDE本地设置文件时,逐个添加,添加前先看一眼git status的输出。

2.3 添加之前,先看一眼 git status

提示:git status是所有Git操作的起点。不管你对仓库做过什么,先跑一下它,永远不吃亏。

git status的输出会明确告诉你:哪些文件是未跟踪(Untracked)的,哪些是已修改(Modified)的,哪些已经进了暂存区(Changes to be committed)。养成"add前看status,commit前看status,push前看status"的习惯,能躲开大部分低级错误。

2.4 误加了文件怎么办

add错了文件,不用慌,也不用去翻各种复杂的reset命令。记住两个就够了:

# 把某个文件从暂存区撤回来,但保留工作区的改动 git reset HEAD <file> # 把暂存区里所有内容撤回来,工作区改动还在 git reset

这里的git reset默认是混合模式(mixed),只会撤掉暂存区的快照,不会动你的文件。真正危险的是git reset --hard,那会把工作区的改动也一并回滚。新手阶段,不遇到特殊情况,建议把--hard从你的字典里划掉。

2.5 .gitignore:从源头堵住不该进仓库的文件

add踩坑,很多时候根子在于没有配好.gitignore。这个文件的作用就是告诉Git:这些目录和文件,你给我当没看见。

一次合理的.gitignore至少要覆盖这么几类:

  • 构建产物:target/dist/build/node_modules/
  • 本地IDE配置:.idea/.vscode/(部分团队会选择提交,看约定)
  • 操作系统文件:.DS_StoreThumbs.db
  • 本地环境配置:.envapplication-local.yml*.local.properties
  • 日志和临时文件:*.log*.tmp

Java后端项目常见配置长这样:

target/ *.class *.log .idea/ .vscode/ *.iml .DS_Store application-local.yml

.gitignore写得越早,后面的麻烦越少。如果是项目已经跑起来之后才补的规则,对于已经被跟踪的文件,.gitignore是无效的,得用git rm --cached把它们从索引里移除,这个操作比较繁琐,所以趁项目初期就把规则定好,性价比最高。

3. commit:提交信息决定代码审查体验,以及commit --amend的正确姿势

3.1 commit的本质:给暂存区拍一张快照

git commit执行后,Git会把暂存区当前的内容压缩成一个新的提交对象,并且记录这次提交的作者、时间、提交信息,以及它的父提交。这种设计让Git的版本历史变成了一条单向链表,每个提交都指向它的前一个提交。

所以git commit不需要传文件参数,它只认暂存区。如果你git commit之后发现自己漏了一个文件没提交,正确操作是先git add漏掉的文件,再commit,或者用git commit --amend把这次提交和暂存区的内容合并成一次新提交。

3.2 提交信息怎么写才不算白写

git commit -m "update"是我见过最多的新手操作,也是团队协作里最让人崩溃的操作之一。过了一个月回头查某个功能是哪次提交引入的,日志里全是"update""fix""提交",谁也说不清。

业界比较通用的规范是Conventional Commits,格式很简单:

<type>(<scope>): <subject> <body>

type常用值:

type含义
feat新功能
fix修复bug
docs文档变更
style代码格式,不影响逻辑
refactor重构,不新增功能也不修bug
test增补测试
chore构建、CI、依赖等杂项

scope是可选的,表示影响范围,比如feat(user): 增加用户注销功能。subject一句话描述清楚,建议用祈使句,不超过50个字符。body部分可以写更详细的背景和影响。

我习惯的写法:

feat(order): 增加订单超时自动关闭功能 - 基于延迟队列实现,超时时间可配置 - 关闭时发送站内信通知用户 - 补充了相关单元测试

3.3 大模型生成commit记录:蹭一个新热点

最近团队里讨论挺多的一个玩法,是用大模型(比如GPT、Claude或者国内的一些大模型服务)帮忙生成commit记录。思路很简单:先把代码diff跑出来,丢给模型,让它按照Conventional Commits规范写提交信息。

比如我经常这么干:

git diff # 查看未提交的改动 # 把diff内容复制给大模型,让它生成commit message

大模型的输出通常比我自己手写规范不少,而且能自动识别是feat还是fix。不过有个坑:diff可能很长,超出模型的上下文窗口,需要自己先过滤掉无关改动,只把核心diff粘贴过去。另外,模型生成的subject偶尔会偏长,我会手动删减一下再提交。用熟之后,这确实能省不少事,尤其是在一个task包含大量琐碎改动的时候。

3.4 git commit --amend:修改最近一次提交的正确姿势

热搜词里出现频率很高的问题:git commit --amend怎么用?这个命令的作用是"修改最近一次提交",具体场景有两个。

场景一:提交完了发现漏了个文件,或者commit信息打错字了。

git add 漏掉的文件 git commit --amend

执行后Git会用暂存区的内容和原来的提交合并,生成一个新的提交,替换掉原来的那个。命令会打开编辑器让你修改提交信息。如果只想改信息,不需要动用暂存区,可以这样:

git commit --amend -m "新的提交信息"

场景二:发现最近这次提交里有不该提交的敏感信息,比如打印了日志、写死了密码。先改文件,再add,再amend,把原来的提交整个覆盖掉。

这里有个必须强调的坑:--amend只适合修改还没有推送到远程的提交。如果这个提交已经push了,而你又amend,本地和远程的历史就对不上了,下次push会被拒绝,强行覆盖远端记录会带来一堆协作上的麻烦(如果这是你自己的分支,并且你确定只有你在用,才可以使用git push --force去同步)。一句话:push出去的东西,宁可新增提交去改,也不要轻易amend。

3.5 提交粒度:一个提交只做一件事

commit信息写得规范,只是第一层。更深一层,是提交的粒度要控制好。

假设你正在做一个用户模块,顺手改了一个工具类的空指针bug,还格式化了一个老文件的缩进。这三件事,理想情况下应该是三个独立的提交,而不是揉在一起。因为代码审查的时候,reviewer想看的是一件事一件事过,混在一起会增加审查难度的风险,而且以后要回溯某次改动,也会被无关内容干扰。

需要说明的是,实际项目里"一个提交只做一件事"执行起来确实有难度,因为工作到一半,改着改着可能就跨了功能。但至少要在commit前检查一下,把不该在这个提交里出现的文件用git reset HEAD <file>拆出去,或者用git add -p交互式地只选择某个文件的某个代码块。

交互式add是个非常实用的技巧,用法是这样:

git add -p

Git会逐个代码块地询问你是不是要暂存,输入y表示加入,n表示跳过,s表示把当前大块再拆成小块。用这个命令,你可以做到一个文件里只提交其中一部分改动,这是精细控制提交粒度的大杀器。唯一的问题是上手有点别扭,建议在专门的测试仓库里练几次,熟练之后效率比跑多个add命令还高。

4. push:本地提交只是开始,推到远端才算数

4.1 push做了什么,为什么有时会失败

git push做的事,是把本地仓库的几个提交上传到远程仓库,同时更新远端分支的指针。成功之后,你的名字会出现在远程提交记录里,别人pull下来就能看到你的代码。

但push恰恰是三个命令里最容易报错的一个。原因很简单:push不是单纯的上传,它涉及"双方"的协作。你提交推送的快照,必须和远端当前的记录合得来。如果远端已经有别人推了新的提交,而你的本地没有pull过,Git就会拒绝push,提示类似 "failed to push some refs"。

解决方式一般是:先git pull,拉取远端最新代码,合并后再push。如果合并出现冲突,还需要手动处理冲突文件。这是Git使用里最常见、也最让新手头大的场景,但它不属于本文三步走的范畴,这里先不展开。

4.2 最经典的报错:fatal: the current branch master has no upstream branch

热搜词里这个报错稳稳上榜,说明遇到的人非常多。完整报错长这样:

fatal: The current branch master has no upstream branch. To push the current branch and set the remote tracking branch, use git push --set-upstream origin master

它的意思是:当前分支没有和远程分支建立关联,Git不知道要推到哪里去。常见于两种情况:一是新克隆的仓库,master分支默认没有上游;二是你本地新建了一个分支git checkout -b feature/xxx,远端还没有对应的分支。

解决办法就是按提示操作:

git push -u origin master

-u--set-upstream的简写,表示推送的同时,将本地当前分支和远程origin/master建立跟踪关系。之后在这个分支上,直接git push就不用再带参数了。

顺便补充一句,从2020年起GitHub等平台把默认分支从master改成了main,所以如果你才初始化的仓库,报错提示里的分支名可能是main而不是master,处理思路是一样的。

4.3 push之前,先处理好认证:Gitee密钥配置和免密登录

要push,首先要解决"你是谁"的问题。Git支持HTTPS和SSH两种协议,两种情况处理方式不同。

HTTPS方式最省事,第一次push时弹出窗口让你输用户名密码(或Token),但每次都要输,很烦。配置credential helper之后,可以把凭据缓存起来:

# 缓存15分钟 git config --global credential.helper cache # 永久缓存(Windows会用Windows凭据管理器,macOS会用钥匙串) git config --global credential.helper store

其中store是明文存在~/.git-credentials文件里,安全性只能说一般,建议在公司个人电脑上用,公共电脑千万别开。

SSH方式的配置流程稍微多一点,但配上之后一劳永逸,也是我推荐的方式。以Gitee为例,完整流程:

# 1. 查看是否已有SSH密钥 ls ~/.ssh/id_rsa.pub # 2. 没有则生成一个新的 ssh-keygen -t rsa -b 4096 -C "你的邮箱@example.com" # 3. 查看公钥内容 cat ~/.ssh/id_rsa.pub

然后把公钥内容复制到Gitee的设置 > SSH公钥页面。测试连通性:

ssh -T git@gitee.com

看到 "Hi xxx! You've successfully authenticated" 之类的提示就说明成功了。之后把仓库的远程地址换成SSH格式:

git remote set-url origin git@gitee.com:你的用户名/仓库名.git

SSH密钥配置完成后,push就不再需要输账号密码,这也是团队里最常见的配置方式。具体到Gitee还是GitHub,操作步骤大同小异,都是生成密钥、填公钥、测试连通三步。

4.4 push的几种拓展用法,按需取用

git push的基础用法是git push origin 分支名,但实际工作中还会用到一些变体。

# 推送到指定远程仓库的指定分支 git push origin feature/login # 本地分支推送到远端并改名(不常用但偶尔会用到) git push origin local-branch:remote-branch # 删除远端分支(慎用,需要确认没有人在用) git push origin --delete feature/old-branch # 推送所有本地分支到远端(一般不推荐这样用) git push --all

这里多提一句git push --force的问题。force推送会直接用本地历史覆盖远端历史,如果不懂原理,很容易把同事的提交冲掉。原则上,只有两种情况可以用:一是你自己独享的分支,确认远端没有别人的提交;二是提交了敏感信息(密码、Token),需要紧急把那次提交从历史里抹掉。除此之外,能用--force-with-lease就用它,这个参数会在覆盖前检查远端是否有人推了新东西,比裸的--force安全很多。

5. 小乌龟和IDEA:把三步走变成鼠标操作

5.1 TortoiseGit小乌龟:Windows用户的图形化救星

命令行门槛对一部分人来说是真实的障碍,这也是为什么TortoiseGit(国内俗称小乌龟)在Windows用户里有那么大的群众基础。它的特点是把Git命令封装成右键菜单,你在资源管理器里就能完成几乎所有常用的Git操作。

小乌龟的三步走,对应到右键菜单上就是:

  1. git add:选中文件或目录,右键,选择TortoiseGit > Add...。它会列出当前工作区所有未跟踪和已修改的文件。
  2. git commit:右键,TortoiseGit > Commit...,弹出窗口里左侧勾选要提交的文件,右侧写提交信息。
  3. git push:右键,TortoiseGit > Push...,选择要推送的分支,点击OK。

小乌龟的提交窗口有个特别好的设计:能看到每个文件的diff内容,还能在同一个窗口里继续勾选/取消文件,对commit粒度的控制反而比命令行更直观。唯一要注意的是,右键菜单的选项比较多,新人刚开始可能会眼花缭乱,其实日常用到的就那几个。

5.2 IDEA里提交代码:绝大多数开发者的主战场

对于用IntelliJ IDEA做开发的人来说,编辑器自带的Git支持已经非常好用,完全不需要切到终端去敲那三个命令。

IDEA的操作路径:项目右键,Git > Commit Directory...(或者直接按快捷键Ctrl+K)。

提交面板左侧列了所有改动文件,可以逐文件勾选或取消勾选。下方的Commit Message输入框有提交信息模板提示,IDEA还会根据你的改动内容智能生成一个简要的message,这个比大部分手写的"update"要强。写完信息点Commit就用本地仓库记录,旁边下拉三角可以选择Commit and Push,一条龙完成commit和push。

如果提交完了发现漏了文件,在IDEA里可以用Amend Commit选项,勾选后会自动带上上次的提交,相当于执行了git commit --amend

IDEA里查看提交历史也很方便:Alt+9打开Git面板,能看到分支图和提交列表,这个视觉化的历史视图,对理解Git的工作原理非常有帮助。建议新手在IDEA里点点提交历史,看看每次commit的diff,慢慢地会对版本管理产生手感。

5.3 图形界面和命令行的关系:不是二选一

有人觉得用图形界面就不够"专业",也有人觉得命令行太吓人,其实这个对立完全不必要。真实开发场景里,大多数人都是混合使用:高频操作在IDE里点点点,遇到冲突解决、分支操作、或者需要细粒度控制时,再切到命令行。

关键在于,不管用什么工具,背后的概念都是同一套。只要你理解了工作区、暂存区、本地仓库、远程仓库的关系,用什么都只是习惯问题。小乌龟和IDEA都不是银弹,它们只是把命令封装成了图形化操作,绕过了记忆命令的门槛,但没有绕过概念理解的坎。换句话说,概念不通的话,工具再友好,遇到 "has no upstream branch" 还是会懵。

6. 一些我踩过坑后形成的操作习惯

文章的最后,分享几个我实际使用Git几年后固化下来的小习惯。不是标准答案,但至少能帮你避开我踩过的那些坑。

先说提交信息。我见过太多人写git commit -m "修改代码",这种信息等于没写。我现在要求自己和团队:不要用-m直接提交,而是用git commit拉起编辑器,写完整的提交信息。虽然每第一次多了十几秒,但在追溯历史时节省的时间是几十倍。

再看push时机。以前我是一commit就push,后来发现频繁push会导致远端分支上提交记录非常碎,比如"fix typo""fix format"这种凑数提交。现在的习惯是:一个功能模块完成后,先在小范围内多次commit,全部自测通过后再一次性push。保持远端历史的整洁度,这对团队同样重要。

还有就是养成push前先pull的习惯。虽然遇到 "git pull 不是 git pull" 的选题能写另外一大篇,但基本盘就是:在团队分支上push之前,先git pull --rebase把你的提交"挪到"最新代码之后,再push。关于rebase和merge的取舍,不同团队有自己的规矩,但不管选哪种,push前pull这个动作本身是必须的。老鸟一般会默认git pull --rebase避免出现多余的merge节点。

最后一个是关于命令别名。把常用命令缩短到极致,能减少反复敲长命令的火气。我在全局配置里加过几个:

git config --global alias.st status git config --global alias.cm commit -m git config --global alias.push push origin git config --global alias.lg "log --oneline --graph --decorate -15"

配置完之后,看一下最近提交的图形化记录,敲git lg就行。这些别名只改了我这侧的体验,不影响仓库内容,团队评审也完全没冲突。

Git这套东西,说穿了没什么高深的,就是几个概念加一堆命令的组合。真正值钱的,是踩过坑之后沉淀下来的那些"肌肉记忆"式的小习惯。add、commit、push三步,看似基础,但把每一步都做扎实的人,交出来的代码和提交历史,一眼就能看出来是靠谱的。希望这篇内容能帮你把地基打得稳一点。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询