news 2026/9/9 17:41:02

Git入门指南:暂存区、提交、撤销与分支操作实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Git入门指南:暂存区、提交、撤销与分支操作实战

先别急着敲命令,打开终端之前,我建议你先花五分钟想清楚一件事:Git 到底是怎么“记住”你的文件的。这篇《Git入门指南(二):基本操作》接在上一篇安装与配置后面,默认你已经装好了 Git、设置好了 user.name 和 user.email。这一篇我会带你走一遍日常开发最高频的“增删改查”操作:git init、git add、git commit、git status、git log、git diff,再顺手处理掉那些“不小心改错了想撤回”“提交完了才发现少了个文件”的尴尬局面。适合刚装完 Git 还不知道第一步干什么的新手,也适合那些会点命令但完全没搞懂“暂存区”到底是什么的朋友——搞清楚这套流转逻辑,后面所有进阶操作都会顺很多。

1. 基本操作的整体思路:先给仓库建三个“格子”

1.1 核心概念:工作区、暂存区、版本库

很多新手学 Git 死在同一个地方:背了一堆命令,却不知道这些命令到底在操作什么。其实 Git 管文件的方式非常朴素,它就给了你三个“格子”。

  • 工作区(Working Directory):就是你电脑上肉眼能看到的那些文件。你新建一个index.html,它在工作区里;你改了一行代码,改动的也是工作区里的文件。
  • 暂存区(Staging Area / Index):这是一个中间缓冲区,英文文档里也叫 Index。你可以把它理解成“准备提交的候车区”。你把文件用git add丢进去,文件就被登记到了暂存区,但还没有真正成为一次版本记录。
  • 版本库(Repository / .git):这是 Git 真正存“快照”的地方,对应你项目根目录下的.git隐藏目录。每一次git commit都会把暂存区里的内容固化成一个版本,存进版本库里。

用做饭来类比:工作区是你案板上刚洗好切好的菜,暂存区是装好盘的小料,版本库则是你按菜单一道道做出来、端上桌的成品。你随时可以往案板上加菜(改文件),也可以决定哪些菜先下锅(add 哪些文件),做得不满意了还可以倒掉重来(撤销操作)。这套“分步走”的设计,是 Git 和 SVN 这类老牌版本控制系统最大的区别。

对应到文件状态上,Git 把文件分成四种状态:未跟踪(Untracked)、已修改(Modified)、已暂存(Staged)、已提交(Committed)。新手最容易迷路的点就在“已修改”和“已暂存”之间——改完文件是 Modified,git add之后变成 Staged,git commit之后变回干净状态。整个流程跑顺了,你就能准确预判每一步执行完,文件在哪个格子里。

1.2 为什么这套设计是合理的

第一次接触暂存区的人大概率都会问一句:“我改完文件直接保存提交不行吗?为什么非要先 add 一下?”这个设计初看啰嗦,实际用起来是救命的。

最直接的好处是可以把一次命令拆成两件独立的事。比如你改了两个文件,一个修了登录页的 bug,另一个是随手改的样式调整,但你只想把 bug 修复作为一次独立提交,样式改动留到下一次——没有暂存区的话,你得先备份、改回、再提交,非常狼狈。有了git add,你可以只把登录页那个文件加进暂存区,提交,然后再把样式文件加进来,提交。提交历史的粒度变细了,以后出问题回滚的时候,你能精准定位到每一次变动。

暂存区还给你留了一个**“反悔缓冲”**。提交是写进版本库的,改起来相对麻烦;但暂存区里的东西想调整很容易,git reset一下就能把文件从暂存区退回到工作区,什么都不丢。换句话说,暂存区是你和版本历史之间的一堵软墙,它给了你一个“提交之前最后看一眼”的机会。

2. 第一次提交:把仓库“跑起来”

2.1 git init 与仓库建置

磨刀不误砍柴工。先把仓库建出来。我习惯每个项目单独建一个目录,然后在目录里执行:

mkdir git-demo cd git-demo git init

执行完git init,Git 会输出一句Initialized empty Git repository in ...,同时在这个目录下生成一个.git文件夹。这个文件夹就是版本库本体,里面装的是 Git 的全部内部数据——你手动删了它,项目的文件还在,但版本历史就全没了。所以.git文件夹要像命根子一样护着,正常情况下不要进去乱动,更别把它提交到 Git 里。

看一下.git目录里这几个关键部分:config文件保存仓库级配置(比如你给这个仓库单独指定的用户名),HEAD文件指示当前处于哪个分支,objects目录存放所有版本快照的压缩数据,refs目录存放分支和标签的引用地址。新手不需要深入理解这些内部结构,但知道它们的存在,以后遇到“为什么不显示提交”“分支怎么不见了”这类问题,排查方向会清晰很多。

顺便说一句,如果你在已有文件的项目里执行git init,也没问题,Git 会把当前所有文件标记为未跟踪(untracked),不会自动帮你提交它们,也不会改动文件内容。也就是说,在任何已有项目里初始化 Git 都是安全的

2.2 add、status、commit:提交三步曲

仓库建好了,往里面放一个文件试试:

echo "hello git" > hello.txt git status

git status会告诉你当前仓库的状态。这时候hello.txt会出现在 “Untracked files” 区域,后面还有一行提示,告诉你git add可以把文件跟踪起来。这个提示本身就是新手最好的老师,刚开始不记命令都行,只要养成“每次都看一下提示再动手”的习惯。

接下来进入核心三步:

git add hello.txt git commit -m "add hello.txt"

git add把文件挪进暂存区,git commit -m把暂存区里的内容固化成一次版本记录。提交之后 Git 会输出类似这样的信息:

[master (root-commit) 5d1f3a0] add hello.txt 1 file changed, 1 insertion(+)

这表示提交成功了,5d1f3a0是这次提交的短哈希值,相当于这次版本的身份证号。此时再执行git status,会看到 “nothing to commit, working tree clean”,工作区干净了,所有文件都已经归档进了版本库。

三个命令里,git add的变体最值得多花点时间熟悉:

  • git add .:把当前目录下所有新增和修改的文件加进暂存区,是最常用的写法。注意它默认不包含被删除的文件(某些版本行为略有差异,新手不用太纠结)。
  • git add -A:把所有改动(包括删除)全部加进暂存区,比add .更彻底。
  • git add -p:进入交互模式,让你逐个 hunk (代码块)选择是否暂存。这个属于进阶技巧,但非常实用,适合“想把一个文件里的一部分改动提交、另一部分留下”的场景。

git commit也有一堆讲究。第一个建议是:尽量每个提交只做一件事。提交信息用动词开头、祈使句,比如fix login bugadd user profile page,不要写update这种什么信息也没提供的词。我见过太多“update”满天飞的仓库,半年后回看历史,完全想不起来那个提交是干嘛的。

2.3 提交之前先看变化:git diff 的两个视角

新手最容易犯的错是“改了一堆、提交完才发现改错了”。git diff就是给你在这个环节把最后一次关的。

git diff本身有两种最常见的用法:

  • git diff:查看工作区与暂存区之间的差异,也就是“我改了但还没 add 的内容”。
  • git diff --staged(等价于git diff --cached):查看暂存区与最近一次提交(HEAD)之间的差异,也就是“我已经 add 了、准备提交的内容”。

我个人的操作节奏是:改完代码先git diff看一遍工作区的改动,确认没问题后git add,再用git diff --staged检查一次准备提交的内容,确认无误才git commit。两步检查看似多花几秒钟,实际能拦下大量的低级错误,比如不小心把调试用的console.log也提交上去了。

拿刚才的例子串一遍:

echo "second line" >> hello.txt git diff git add hello.txt git diff --staged git commit -m "append second line"

你会看到第一次git diff显示的是工作区里那行新增内容,git diff --staged显示的则是暂存区里等待提交的内容。数据源不同,展示的内容阶段也不同,把这两个命令练成肌肉记忆,基本就不会再出现“提交了不是预期内容”的事故。

3. 版本的回溯与撤销:把做错的事“反悔”回来

3.1 git log:先看历史再动手术

要撤销什么,首先得知道历史长什么样。git log就是版本历史的说明书。

最基本的用法:

git log

会带出完整的提交记录、作者、提交时间和提交信息。不过这个输出在提交多了以后有点冗长,我日常最常用的是这两个变体:

git log --oneline git log --graph --oneline --all

--oneline把每个提交压缩成一行:短哈希 + 提交信息,扫一眼就知道历史脉络。--graph会画出分支和合并的路线图,配合--all可以看到所有分支的提交情况,排查问题时的信息量非常足。

在开始任何撤销操作之前,养成先跑一遍git log的习惯。你得先确认要回的版本是哪个哈希值,别凭着记忆猜。哈希值也可以不用复制完整的那一串,Git 支持前缀匹配,复制前 6-7 位就足够唯一了。

3.2 git reset:回到过去的三种模式

git reset是撤销操作的“重武器”,它的核心作用是把当前分支的 HEAD 指针挪到历史某个位置,顺便决定暂存区和工作区跟不跟着变。理解它之前,先记住一个口诀:reset 的三种模式,管的东西不一样多

  • git reset --soft <commit>:只移动 HEAD 指针,暂存区和工作区都不动。也就是说,你 reset 之后,之前 add 的内容还在暂存区,文件内容也没变,只是版本历史回退了。适合“提交完了发现忘了 add 某些文件,想重新提交”的场景。
  • git reset --mixed <commit>(默认模式):移动 HEAD,并且把暂存区重置成和这个 commit 一致,但工作区文件不变。这是最常用的模式,适合“提交完发现内容不对,想撤销提交但保留文件修改”。
  • git reset --hard <commit>:移动 HEAD,暂存区和工作区全部重置成目标 commit 的状态。这是最危险的操作,用它等于把目标 commit 之后的所有文件改动全部抹掉,没有后悔药。

举一个实际场景:我提交了一个文件,但马上发现提交信息写错了,或者漏掉了一个文件。这时候我通常用--soft回退一步:

git reset --soft HEAD~1

HEAD~1表示“上一个提交”。执行完之后,提交历史往前退了一条,但暂存区里还保留着你上一次提交的全部内容,文件也原封不动。这时候你可以重新git add漏掉的文件,再次git commit -m "正确的提交信息",一次干净利落的“后悔药”。

如果你做了一次提交,但想保留文件改动、把提交历史彻底抹掉,用默认的--mixed

git reset HEAD~1

文件改动会回到工作区,所有暂存状态被清空,提交历史少了一条。你可以在工作区继续修改,想好之后重新 add、提交。

关于--hard的警告:不要轻易用,用之前至少确认三遍。如果你连续提交了三五次,发现方向全错了,想全部推倒重来,可以用--hard回退到最初的提交点,但前提是你确定中间这些改动全都是废的。如果只是某个文件的改动是废的,你更需要的可能是git checkout -- <file>或者git restore <file>这种单文件恢复命令。哦对,想完全抹掉提交的时候,还可以配合git push --force,但那是协同场景,等以后讲冲突处理的时候再展开。

3.3 git revert:用一次新提交来“抵消”错误提交

git reset有个致命短板:它会改写历史。如果你已经把这个提交推送(push)到了远程仓库,别人也已经拉取(pull)了,你 reset 之后再 push,必然引发冲突和混乱。这种多人协作场景下,正确做法是用git revert

git revert的逻辑不是“把头移回去”,而是“做一次反向操作”。比如你提交了一个文件,里面加了一行console.log,你执行:

git revert HEAD

Git 会生成一次新的提交,这次提交的内容刚好抵消掉上一次提交的改动——相当于把多出来的那行删掉。版本历史是一条往前延伸的线,没有发生过“历史重写”,远程协作的同伴 pull 下来也不会出问题。

配套git log一起用,你还能精准找到某个历史提交并把它 revert:

git log --oneline git revert 5d1f3a0

执行时 Git 可能会打开编辑器让你填提交信息,默认信息是Revert "原来的提交信息",直接保存退出就好。

新手看到这里大概率会纠结:“那我到底该用 reset 还是 revert?”我给你一个傻瓜判断标准:如果这个提交只在你本地,用 reset;如果已经推送到了远程,用 revert。前者是你自己的事怎么都好说,后者涉及别人的本地历史,绝对不能重写。

3.4 文件的删除与改名:让 Git 知道“少了一个文件”

你可能会说,删文件不就是rm一下嘛,有什么好讲的?这里坑点相当隐蔽。

假设你仓库里有个hello.txt,你直接在文件管理器里把它删了,再去git status,Git 会提示你有一个 “deleted” 状态的文件。如果这时候直接git commit -m "delete hello.txt",Git 会拒绝,因为删除操作还没有被记录到暂存区。正确做法是:

git rm hello.txt git commit -m "delete hello.txt"

git rm会同时完成“删除工作区文件”和“把删除动作暂存”两件事,之后提交就顺理成章了。如果你是用系统命令删的,也可以用git add -A把删除动作补进暂存区,效果一样。

改名同理。直接在文件管理器里把hello.txt改名成greeting.txt,Git 会把它理解为“删除 hello.txt + 新增 greeting.txt”,提交后历史里会有两条记录。更优雅的做法是:

git mv hello.txt greeting.txt git commit -m "rename hello.txt to greeting.txt"

Git 内部的“重命名检测”其实不看命令,而是看文件内容相似度,git mv只是帮你省去“先 rm 再 add”的步骤。但养成用git mv的习惯,好处是别人看你的操作记录时,一眼就能看出这是一个改名动作,而不是“删了一个又加了一个”,可读性完全不同。

4. 分支操作:把开发线“岔开”再“合拢”

4.1 为什么分支操作是 Git 的“大招”

如果只学一个 Git 功能,我会毫不犹豫选分支。Claude、GitHub Copilot 这些工具再强,也替代不了分支给你带来的并行能力。

给一个最容易理解的类比:你在同一台电脑上维护着两个版本的简历,一份是“面向外企的英文版”,一份是“面向国企的中文版”。如果只有一份文件,每次改版本前都得先复制一份,特别容易把两版搞混。分支就是 Git 帮你实现“同一份文件,多个修改方向并存”的机制——你在一个分支上改英文版,另一个分支上改中文版,互不干扰,什么时候想合并了再合。

分支在 Git 内部其实只是一个指向提交的指针,创建和切换的成本极低,这正是它比传统版本控制系统的“拷贝目录”方案高明的地方。哪怕你的仓库有几百个分支,也不会多占多少磁盘空间。所以放开了建分支,建错了删掉也不心疼。

4.2 分支的新建、切换与合并

继续用我们的git-demo演示。先看看现在在哪个分支上:

git branch

默认情况下你会在master(某些新版 Git 默认是main)分支上。新建一个分支并切过去:

git branch feature-login git checkout feature-login

这两条命令可以合并成一条:git checkout -b feature-login。我平时几乎都这么写。如果你用的是 Git 2.23 以上版本,更推荐用新命令git switch -c feature-login——checkout同时肩负着“切分支”和“恢复文件”两个职责,职责不单一,新手容易混淆,switch就是专门负责切分支的。

切到feature-login分支后,改点东西:

echo "login page" > login.html git add login.html git commit -m "add login page"

然后切回主分支:

git checkout master

注意,你切回主分支后,login.html这个文件就“消失”了——别慌,它只是在feature-login分支里存在,主分支的历史里没有这个文件,所以工作区里看不到它。这正是分支隔离性的体现:在不同分支上工作,工作区的内容完全不同

接下来把功能分支合并回主分支:

git merge feature-login

Git 会把feature-login上多出来的提交合进来,如果两个分支修改的文件互不冲突,合并是自动完成的。这时候再看一眼git log --oneline --graph,你会看到一条从主分支分出又合回的轨迹,成就感直接拉满。

4.3 第一次遇到冲突怎么办

不冲突是幸运,冲突才是常态。当两个分支改了同一个文件的同一行,Git 就没法替你决定保留哪个版本了,它会告诉你:

CONFLICT (content): Merge conflict in login.html Automatic merge failed; fix conflicts and then commit the result.

这时候打开冲突文件,你会看到类似这样的内容:

<<<<<<< HEAD <div>当前分支的内容</div> ======= <div>feature-login 分支的内容</div> >>>>>>> feature-login

<<<<<<< HEAD=======之间是当前分支的内容,=======>>>>>>> feature-login之间是对方的改动。你要做的是手动编辑这个文件,决定最终保留成什么样,然后删掉标记符号,最后:

git add login.html git commit -m "resolve merge conflict"

记住一个关键点:冲突解决完成后,提交一次就可以了,不需要再手动跑git merge。这一节只讲最基础的处理流程,像 git rebase 那种更高级的“改写历史式合并”,等后面讲“进阶操作”时再展开,入门阶段掌握merge+ 手动解决冲突,已经足够覆盖绝大多数日常需求。

4.4 远程仓库与克隆:把代码搬到另一台电脑上

“基本操作”讲到这里,理论上你已经可以在本地愉快地玩耍了。但版本控制最大的价值在于协作和备份,这就要用到远程仓库了。这一节只讲两个最基础动作:克隆和推送。

从远程拉一个项目到本地:

git clone https://github.com/username/repo.git

执行完,当前目录下会出现一个repo文件夹,里面既包含全部代码,也包含完整的版本历史。这一步做完,你就拥有了这个项目的完整本地副本,之后的git loggit branchgit addgit commit全部照常使用。

把一个本地仓库推到远程,需要先关联远程地址:

git remote add origin https://github.com/username/repo.git git push -u origin master

第一次推送时-u参数会把本地分支和远程分支建立关联,之后就可以简写为git push。别人改完了代码,你拉取最新内容:

git pull

这里稍微提一句:git pull实际是git fetch+git merge的简写,先抓取远程的新提交,再合并到你当前分支。入门阶段你可以把pull当成“从远程把最新代码拿下来”的一站式命令,但心里要清楚它是两个动作的组合,以后遇到 “pull 之后冲突了” 的情况才能知道是在哪一步出的问题。

5. 常见问题与排查技巧实录

5.1 新手高频问题速查

下面这些问题,是我在带新人时几乎每周都会遇到一次的经典坑。整理成一张速查表,踩坑的时候直接对着找。

现象原因解决办法
git commit后敲了git log发现提交人有误全局 user.name/user.email 没配好git config --global重新配置后再提交
输入git status后中文文件名显示成\xxx\xxx的转义码Git 默认对非 ASCII 文件名做了转义执行git config --global core.quotepath false
git add .后提交,最后发现少了一个文件.gitignore把那个文件忽略了检查.gitignore,用git add -f <file>强制添加
执行git reset --hard之后想找回被删的改动没有备份,git 本身也无法直接找回日常多用git stashgit branch备份,commit 前先git diff确认
在 Windows 下复制粘贴命令时卡住,键盘输入不进去终端把 Ctrl+C 等快捷键拦截了,处于“等待输入”状态按 Enter 结束输入,或者 Ctrl+C 先退出当前命令,重新跑
分支太多,不想看到一大串分支列表太长,影响判断git branch -v看带最新提交的分支,或者用git branch --merged看已合并分支

这里面有两个点想单独强调一下。

第一,误操作git reset --hard后的“后悔药”不是完全没有。如果你在 reset 之前一刻做过git stash或者创建过分支,改动还能从那里恢复。但如果你什么都没做,那真的是神仙难救。所以我的习惯是:大改动之前,先git checkout -b backup-temp开个临时分支,或者git stash一下,给自己留条后路。

第二,“提交了不该提交的文件”是个超级常见问题。比如把node_modules这种依赖目录、.env这种包含密钥的环境变量文件提交进了仓库。前者会让仓库变得巨大,后者是安全事故。解决办法:优先写.gitignore,在仓库初始化时就把它写好;如果已经提交了,用git rm --cached <file>把文件从 Git 跟踪中移除(保留本地文件),然后提交一次,再做一次 push。

5.2 两个值得养成的配置小习惯

操作层面之外,有两个配置项能显著提高日常使用体验。

第一个是配置默认编辑器。如果你git commit不带-m,Git 会打开一个文本编辑器让你填提交信息。很多新手在 Vim 里卡住不知道怎么退出,其实只要配置成自己熟悉的编辑器,体验立刻起飞。Windows 下配置成 VSCode:

git config --global core.editor "code --wait"

之后不带-m提交时,VSCode 会自动打开一个临时文件,写完保存、关掉标签页,提交就完成了,全程可视化,再也不怕卡在 Vim 里。

第二个是配置命令别名。如果你觉得git statusgit log --graph --oneline敲着太长,可以设置别名:

git config --global alias.st status git config --global alias.lg "log --graph --oneline --all"

之后git stgit lg就能直接用了。别小看这个习惯,命令敲得越顺手,你越愿意频繁提交、频繁查看状态,版本管理的效果也就越好。

写到这里回头看,Git 基本操作其实就两件事:先把文件流转的“三个格子”装进脑子里,然后把“增删改查 + 后悔药 + 分支”这几个动作练熟。命令本身几个小时就能记住,真正区分熟手和新手的是操作习惯——提交前先看 diff、reset 前先留后路、提交信息写谁都看得懂的话、该开分支时毫不犹豫地开。这些习惯比记住任何一条命令都金贵。至于 rebase、stash、cherry-pick、远程分支协作那些进阶玩法,等你把这一篇的命令都练到不用过脑子的程度,自然就会觉得水到渠成,到时候再看下一篇,就轻松多了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 17:40:39

C语言内存管理从入门到实战:栈与堆、malloc/free与排查工具

写C语言最痛苦的事情是什么&#xff1f;我入行十年&#xff0c;面试过上百个候选人&#xff0c;也带过不少实习生&#xff0c;大家答案不一&#xff0c;但排在第一名的大概率是内存管理。段错误、野指针、内存泄漏&#xff0c;随便拎一个出来&#xff0c;都能让一个号称熟练C语…

作者头像 李华
网站建设 2026/9/9 17:38:24

Windows 11 系统精简实战:镜像瘦身到 2.2GB,开机只需 28 秒

Windows 11 系统精简实战&#xff1a;镜像瘦身到 2.2GB&#xff0c;开机只需 28 秒 【免费下载链接】tiny11builder Scripts to build a trimmed-down Windows 11 image. 项目地址: https://gitcode.com/GitHub_Trending/ti/tiny11builder 让老设备跑 Windows 11&#x…

作者头像 李华
网站建设 2026/9/9 17:37:53

88个经典Android应用打包下载:APK校验与批量安装全攻略

简介&#xff1a;88个经典Android应用源代码打包&#xff0c;面向Android开发初学者和进阶者&#xff0c;是系统研究组件架构与最佳实践的实用资料库。压缩包为RAR格式&#xff0c;大小21.27MB&#xff0c;涵盖Activity、Service、BroadcastReceiver、ContentProvider四大组件及…

作者头像 李华
网站建设 2026/9/9 17:36:32

Cursor 试用限额重置完整教程:3 分钟换回免费额度的 5 个步骤

Cursor 试用限额重置完整教程&#xff1a;3 分钟换回免费额度的 5 个步骤 【免费下载链接】go-cursor-help 解决Cursor在免费订阅期间出现以下提示的问题: Your request has been blocked as our system has detected suspicious activity / Youve reached your trial request …

作者头像 李华
网站建设 2026/9/9 17:35:44

通义千问多模态AI免费实测:打通文本图像视频生成链路

最近这几个月&#xff0c;多模态 AI 的工具和模型更新得实在太密了。但一个很有意思的现象是&#xff1a;很多开发者的困惑&#xff0c;并不是“不知道谁家模型强”&#xff0c;而是“我到底该怎么把它接进自己的项目里”。文本生成看起来很好用&#xff0c;可一旦要把它和图像…

作者头像 李华