ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

15个Git核心命令全解析:从分支合并到回滚实战

15个Git核心命令全解析:从分支合并到回滚实战 先把话放在前面Git 这个工具你迟早要学而且越早学越划算。我见过太多人用图形化工具点了一两年的提交真到了命令行窗口一片黑、同事让你处理分支冲突、或者不小心把错误代码推到远程的时候整个人是懵的——因为图形界面把最关键的一层逻辑给藏起来了。这篇写的是 15 个你一定会用到的 Git 核心命令覆盖从拉代码、改代码、存档、开分支、合并、推到远程到推错了怎么回滚的完整链路。适合刚入门但不想停留在“点按钮”阶段的人也适合用过一阵子但某些命令一直没搞明白的朋友。我会尽量把每个命令背后的原理讲清楚而不是让你死记硬背。为了不让文章变成命令字典我会用一个贯穿全文的场景来做例子假设你正在维护一个个人博客项目 momo-blog从零开始用 Git 管理它然后逐步推进到团队协作时的分支合并场景。这样每个命令是“为什么用”“怎么用”“踩过什么坑”串起来的而不是孤立的一条条语法。1. 先把地基打牢Git 装好之后你需要理解这一点1.1 Git 的三个核心区域搞懂它能解决一半困惑很多人学 Git 学得痛苦是因为一上来就背命令但完全不理解这些命令在操作什么。其实 Git 的所有核心命令都在围绕三个区域转工作区、暂存区、版本库。你可以把工作区理解成你正在编辑的文件夹就是你在 IDE 里看到的那些文件暂存区是一个“待提交清单”你告诉 Git “这些改动我等会儿要存档”改动就会先放到这里版本库则是 Git 真正存“快照”的地方每提交一次就会生成一个不可变的版本记录。用生活类比的话工作区是你桌子上的草稿纸暂存区是你的“待归档文件夹”版本库就是已经锁进档案柜的正式文件。为什么要先讲这个因为后续的add、commit、reset、stash这些命令本质上都是在三个区域之间移动内容。比如git add是把工作区的改动放进暂存区git commit是把暂存区的内容固化成版本库里的一个快照。你把这条链路想明白了后面一大半命令都不用死记。很多新手搞不清楚“为什么我 commit 了文件还是显示 modified”就是因为提交前忘了add或者改了文件但没告诉 Git 要暂存这次的改动。1.2 安装与基础配置装完之后必须先做这两件事Git 的安装本身不复杂各平台都有成熟的方案。Windows 上可以直接去官网下载安装包也可以打开终端用 winget 安装macOS 上最简单的是通过 Homebrew 安装Linux 发行版一般直接用系统包管理器就行。这里我就不展开每个平台的点击流程了网上一搜一大把教程但你做完这一步之后有两件事是被大量教程忽略、却非常影响后续体验的。第一件事是配置用户名和邮箱。Git 的每次提交都会记录“是谁提交的”这个信息就来自user.name和user.email它不会自动读取你的系统账号。如果你不配置提交时 Git 会报错或者生成一段看不懂的默认信息。配置命令很简单git config --global user.name 你的名字 git config --global user.email youexample.com--global表示全局生效对当前用户的所有仓库有效。如果你在公司项目里需要用不同的身份可以在具体仓库里去掉--global再设置一遍它会覆盖全局配置。第二件事是和换行符打交道。Windows 和 Linux/macOS 对文本换行的处理方式不一样如果你和队友跨平台协作经常会遇到“我明明只改了一行为什么 diff 里整个文件都变了”的鬼故事。解决方法是设置core.autocrlf。Windows 上建议设为true提交时自动把 CRLF 转成 LFmacOS/Linux 上建议设为input。这样至少能避免一大类换行符引发的假冲突。你可以在终端里查看当前所有配置git config --list1.3 15 个核心命令全景表先有个整体印象在看接下来的详细拆解之前我先把 15 个命令和它们的一句话作用整理成一张表。你可以先扫一遍有个整体印象然后带着问题往下读。命令核心作用最常用的场景git init在当前目录初始化一个 Git 仓库本地新项目开始做版本管理git clone把远程仓库完整复制到本地接手一个已存在的项目git status查看当前工作区的改动状态改了一堆文件之后确认到底改了什么git add把文件改动加入暂存区提交前选择要纳入版本的文件git commit把暂存区内容固化成一次版本记录完成一个阶段性小目标正式存档git log查看提交历史复盘项目演进找某次改动的原因git diff对比不同版本之间的差异提交前检查自己到底改了什么git branch创建、删除、查看分支开启一个新功能或修复任务的独立开发线git switch切换当前分支在功能分支和主分支之间来回切换git merge把一个分支的变更合并进当前分支开发完功能并测试通过后合回主分支git remote管理远程仓库地址查看、添加、删除远端仓库源git push把本地提交推送到远程仓库让队友看到你的最新代码git pull拉取远程最新代码并合并到本地开始一天工作前同步远端状态git reset撤销提交或取消暂存回退版本提交错了、想回退到之前的某次版本git stash临时保存未提交的改动切换分支前把改了一半的代码先收起来这 15 条覆盖了本地管理、分支操作、远程协作、撤销回滚四大板块。接下来逐个板块展开。2. 本地仓库从 0 到 1初始化、查看与提交2.1 git init 和 git clone两种拿到仓库的方式git init起步很简单。你在终端里进入项目目录执行git init这个命令会在当前文件夹里生成一个隐藏的.git目录所有的版本信息都存在这里。执行完之后你可以用git status看仓库状态此时整个项目的所有文件都会显示为“未跟踪”。这里有个容易踩的坑很多人把git init执行完就以为项目“被管理了”其实这只是打了个地基。你还需要至少完成一次add和commit项目才算真正进入版本管理状态。另外初始化时最好提前写好.gitignore文件把不需要纳入版本管理的目录排除掉。比如node_modules、dist、.idea、.DS_Store这类文件如果不忽略每次提交都会产生大量无意义的改动还会拖慢仓库速度。.gitignore看起来不起眼但在真实项目里几乎是必备品。git clone则是另一条路从远程仓库复制一个完整项目到本地。它的好处是仓库自带完整的版本历史你不需要关心初始化配置拿来就能接着干活git clone https://github.com/example/momo-blog.git如果想克隆到指定目录名直接在后面加一个参数即可git clone https://github.com/example/momo-blog.git my-blog这里有个小提示克隆时优先使用 SSH 地址还是 HTTPS 地址如果你在 GitHub、Gitee 或公司 GitLab 上用 SSH 配置好了密钥SSH 地址会更省心因为它不用每次推送都输密码还天然避开了一些网络问题。具体的 SSH 配置排查我在第四章会单独讲。2.2 git status、git add、git commit提交三连的完整节奏我见过的最常见的错误提交习惯是一次性把所有文件全部add然后随便写一句提交信息。先说正确节奏再说为什么。正常的一次提交应该分三步走。第一步用git status看清当前状态git status输出会告诉你哪些文件被修改了、哪些还没被跟踪、当前在哪个分支、和远程分支差多少个提交。这是一个高频命令它不是用来“看看而已”的而是你每次操作前都要执行的“方向盘”。第二步用git add把需要提交的改动加入暂存区。你可以精确到单个文件git add src/components/Header.js也可以按目录或类型加入git add src/ git add *.css或者粗暴一点全加git add .我不建议无脑git add .尤其是在团队项目里。你可能会把调试日志、临时文件、甚至不想提交的密码配置一起加进来。正确的习惯是提交前问自己一句这次提交的主题是什么只把相关的改动加进来。第三步执行git commitgit commit -m 提交信息-m后面的信息是要给未来的自己和其他协作者看的。好的提交信息不是“更新”或者“修改了一堆东西”而是像一条清晰的笔记“完成登录页表单校验”、“修复移动端菜单无法收起的问题”。如果你能坚持一次提交只围绕一个主题后续用git log找问题会轻松得多。提交完成后git status会变成干净状态git log里就会出现你刚才提交的那条记录。2.3 git log 和 git diff复盘与对比的利器git log是查看历史记录的命令。默认输出的格式很长每次提交会显示作者、日期、完整提交信息。在日常使用中我一般会用这几个参数把它压缩成更易读的形式git log --oneline --graph --all--oneline让每次提交只显示一行--graph用字符画展示分支的合并轨迹--all把各个分支的记录都展示出来。配合起来你能一眼看清项目的演进路线以及各个分支之间是什么关系。git diff则是对比差异的命令。它的用途非常广泛我挑三个最高频的说法。第一个是提交前看工作区相对暂存区改了什么git diff第二个是看暂存区相对上一次提交改了什么git diff --cached第三个是对比两个分支或者两次提交之间的差异git diff main develop git diff abc123 def456说个我自己的经历有一回我花了一下午改一个样式问题怎么改都不对后来用git diff对比了最近几次提交才发现是某次“顺手”的改动把一个公共样式给覆盖了。这就是diff的核心价值——它能帮你把“改了什么”从模糊记忆变成明确证据。尤其在排查“之前还能用、现在突然坏了”的问题时先看 diff比瞎猜高效得多。3. 分支操作并行开发的核心武器3.1 分支的本质与 git branch 的日常用法先说破一个常见误解分支并不是把代码复制一份它本质上只是一个指向某个提交的“指针”。你创建新分支时几乎不占额外空间只是在当前的提交上多贴了一个标签。你在新分支上继续提交这个标签就会跟着往前走而原来的分支还停在原地。这样两个人可以同时从同一个起点出发各自往前开发互不干扰。git branch是管理分支的基础命令。不带参数直接执行会列出当前所有本地分支当前分支前面会标一个星号git branch创建新分支的写法是git branch feature/dark-theme这条命令只创建分支不会自动切换过去。如果希望创建并切换一步到位接着看下一节。删除分支的写法是git branch -d feature/dark-theme注意-d只能删除那些已经被合并过的分支。如果你强制删除一个还没合并的分支需要用-D。这个设计是 Git 在保护你分支里如果有未合并的提交直接删掉意味着那些代码可能会彻底丢失。3.2 git switch 与 git checkout切换分支的正确姿势Git 在 2.23 版本之后推荐用git switch来切换分支它的语义比git checkout更明确专门负责“换分支”这件事git switch feature/dark-theme创建并切换可以连在一起git switch -c feature/dark-theme你可能会问那git checkout是不是就没用了也不是。checkout除了切分支还能用来还原文件。比如某次改动改坏了想把这个文件恢复到上一次提交的状态git checkout -- src/utils/format.js这条命令非常危险它会丢弃工作区里的未提交修改且没有确认提示。执行前最好确定你真的不要这些改动了。切分支时有一个高频踩坑场景你在 feature 分支上改到一半突然想起有个紧急 bug 要去 main 分支修直接执行git switch main。如果工作区有未提交的改动Git 通常会阻止你切换或者把改动带过去取决于改动是否和两边分支冲突。我的习惯是切换分支前先看一眼git status要么提交掉要么用下一章的stash存起来绝不让仓库处于“半脏”状态到处跑。3.3 git merge合并分支与冲突处理实战开发完新功能之后要把 feature 分支的改动并回主分支。先切回目标分支再执行合并git switch main git merge feature/dark-theme如果两个分支的提交历史是“一条直线”Git 会执行快速前进合并。什么概念就是 main 分支没有产生新提交feature 分支在原来基础上一直往前走Git 直接把 main 的指针移动到 feature 所在的提交即可速度快且不需要额外操作。但如果 main 分支在 feature 分支从它分出去之后也有了新提交Git 就需要做一次真正的三方合并并生成一个“合并提交”。大多数情况下 Git 会自动完成合并但偶尔会遇到冲突。冲突的表现是合并后 Git 告诉你哪些文件冲突了你打开文件会看到类似这样的内容 HEAD 这里是当前分支的内容 这里是传入分支的内容 feature/dark-theme你需要手动决定保留哪边或者两边都保留然后把冲突标记删除保存文件。之后执行git add . git commit -m 解决与 dark-theme 分支的冲突注意解决冲突不是在编辑器里乱改一通就完事。我的建议是遇到冲突先搞清楚两边各自的意图尤其是涉及公共逻辑的改动最好和写另一边代码的同事确认一下。纯机械地“选一边”容易把别人的功能删没这是合并中最容易埋雷的地方。4. 连接远程仓库团队协作不再靠 U 盘4.1 git remote管理远程仓库地址本地仓库要和远程协作第一步就是建立关联。git remote就是管这件事的。查看当前关联的远程地址git remote -v如果项目是clone来的远程地址一般已经自动配置好了名字叫origin。origin只是一个约定俗成的名称你完全可以改成别的名字但没必要特意折腾。本地初始化的仓库需要手动添加远程git remote add origin gitgithub.com:example/momo-blog.git如果远程地址换了可以修改git remote set-url origin gitgithub.com:example/momo-blog-new.git在真实团队里常见的仓库设置是维护一个origin作为主要远程仓库。如果是参与开源项目往往还需要 fork 一份到自己的账号下然后把自己的 fork 加为origin把原项目地址加为upstream用来同步上游的新变化。理解remote这个命令之后这些操作就顺理成章了。4.2 git push 与 git pull推送拉取的底层逻辑git push是把本地提交推到远程。第一次推送一个新建分支时我建议加上-u参数git push -u origin feature/dark-theme-u表示建立本地分支与远程分支的追踪关系之后你在该分支上直接执行git push和git pull就不用再指定参数了。git pull的作用是把远程的最新改动拉到本地。很多人只知道“它能把代码更新到最新”但没意识到它其实是两个操作的组合先执行git fetch获取远端的最新 commit 记录再执行git merge把远端记录合并到当前分支。理解了这一点你就能想明白很多现象为什么 pull 会触发冲突因为它本质上是 merge。为什么有时候 pull 之后你的本地会多出一个合并提交因为 fetch 下来的远端分支和本地分支分叉了。如果只想看远端有什么改动而不想合入可以单独执行git fetch然后用git log origin/main查看。团队协作里最推荐的流程是动手改代码之前先git pull同步一次改完之后提交再 push。如果你改了很长时间push 之前最好再 pull 一次看看这段时间有没有人推了新改动有的话先解决冲突再 push。这个习惯能避免大量互相覆盖的尴尬。4.3 SSH 认证失败最经典的求助场景排查实录如果你在推送或拉取时遇到类似Permission denied (publickey)、ssh: connect to host ... Connection refused之类的报错大概率是 SSH 认证出了问题。这是“ssh认证失败”这个话题下最常被搜到的问题我把它单拎出来讲一遍完整的排查思路。第一步先确认仓库的远程地址用的是 SSH 而不是 HTTPS。如果是 HTTPS 地址走的是用户名密码或令牌认证跟 SSH 密钥无关git remote -v如果是 SSH 地址会显示类似gitgithub.com:example/momo-blog.git的格式。第二步用 SSH 命令测试和远程服务器的连通性ssh -T gitgithub.comGitHub 如果认证成功会返回一条欢迎语。如果返回Permission denied (publickey)说明 SSH 密钥没被识别。第三步检查你有没有生成 SSH 密钥、密钥是否被 ssh-agent 加载ls ~/.ssh/ ssh-add -l.ssh目录下如果只有id_rsa和id_rsa.pub还好如果没有星号密钥或者 agent 里没有密钥就需要先生成再添加ssh-keygen -t ed25519 -C youexample.com ssh-add ~/.ssh/id_ed25519第四步把公钥内容添加到你使用的 Git 托管平台后台。查看公钥cat ~/.ssh/id_ed25519.pub复制输出的一整行粘贴到 GitHub/Gitee/GitLab 的 SSH keys 设置页面。最后再用ssh -T验证一次。这里有个我踩过的坑Windows 上换了新电脑后SSH agent 服务没有自动启动导致ssh-add每次都要手动执行很烦。我的解决办法是直接编辑~/.ssh/config文件把密钥路径写死并把AddKeysToAgent和UseKeychain打开。这样只要密钥文件存在SSH 就能自动找到不用再手动加载。这个小配置在很多教程里都不会讲但实际操作中非常省心。5. 后悔药与时间旅行reset、revert、stash 的实战用法5.1 git reset软、硬、混合三种模式的区别git reset是“回退到历史版本”的核心命令也是最容易让人困惑的命令因为它可以带三种模式--soft、--mixed、--hard。我用一个表格帮你理清它们的区别。模式移动 HEAD 指针暂存区工作区典型使用场景--soft是保留保留撤销上一次 commit但保留改动可以重新提交--mixed默认是清空保留退掉暂存状态重新组织要提交的文件--hard是清空清空彻底回到某个历史版本丢弃所有改动举例说明。假设你刚提交了一次 commit立刻发现提交信息写错了或者想拆成两次提交用--soft最好git reset --soft HEAD~1执行后版本库里的提交记录被撤掉一条但你的改动还留在暂存区可以重新 commit。HEAD~1的意思是“前一个提交”如果你要后退多个就改成HEAD~3或者具体的提交号。如果你只是想取消暂存保留工作区改动用默认的--mixedgit reset HEAD~1改了之后文件还在但git status会显示为“未暂存”。--hard则是威力最大的模式它会直接把工作区和暂存区都恢复到目标版本的状态。执行git reset --hard HEAD~1之后你刚才改的东西会从工作区里彻底消失git reset --hard HEAD~1这几乎等于“穿越回过去”所以要极度谨慎。我的习惯是执行--hard之前先确认没有需要保留的未提交改动或者提前用git stash把改动存一份。如果你已经执行了也不要慌下一节我会讲怎么用 reflog 找回来。5.2 git stash临时收藏未提交的工作git stash是“临时保存现场”的命令。它的适用场景非常典型你正在 feature 分支上改一个功能改到一半老板说线上有一个紧急 bug需要你现在就去 main 分支修。这时候你既不能提交半成品会导致逻辑不完整又不能丢弃这些改动怎么办用 stashgit stash执行后工作区和暂存区的改动会被暂时收进一个“储藏区”工作区恢复成干净状态。你可以放心地切分支、改 bug、提交、切回来。回到功能分支之后把改动恢复出来git stash poppop会把最近一次储藏的内容取出来并同时从储藏区删除。如果你需要保留储藏内容、同时应用到另一个分支可以用git stash apply。查看当前储藏列表git stash list我的经验是stash 适合短期保存比如几小时、一两天。如果长期放着不管你会忘记里面存了什么甚至会出现切换分支后 pop 导致冲突的麻烦。另外stash 里的内容默认不会跟随 push 到远程别指望拿它当异地同步工具。5.3 误删之后的后悔药reflog 与 revert 的正确选择先说git reflog。它是一个经常被忽略但极其有用的命令记录着你在本地执行过的所有 HEAD 移动历史包括reset、merge、commit等操作。即使你执行了git reset --hard丢掉了提交只要git reflog里还有记录就还能找回来git reflog你会看到类似这样的输出abc1234 (HEAD - main) HEAD{0}: reset: moving to HEAD~1 def5678 HEAD{1}: commit: 完成暗色主题如果你后悔 reset 了可以根据记录里的提交号恢复到之前的状态git switch -c recover-branch abc1234建议先创建一个新分支去检查恢复的内容确认无误后再决定是否合并回主干。这个操作不会破坏当前状态是我处理“手滑误删”的首选方式。再说git revert。它和reset有本质区别reset是移动分支指针相当于“改写历史”revert是生成一个新的提交来反向应用某个旧的提交相当于“添加一个撤销记录”。在团队共用的分支上千万不要用reset去回滚已经推送到远程的提交那样会破坏其他人的本地历史。正确做法是用 revert 生成一次新的提交然后正常 pushgit revert abc1234因为 revert 不会改写历史而是追加一条提交这样团队里所有人的仓库都能平滑地同步不会出现大量冲突或“强制推送把队友线搞断”的情况。6. 高频问题排查速查表6.1 常见报错与操作场景对照我在实战中遇到过的 Git 问题五花八门但绝大多数都能归到几个典型类别里。整理成一张速查表方便你以后遇到报错时直接对照。报错或现象常见原因排查与解决Permission denied (publickey)SSH 密钥未配置或未加载按第四章的步骤检查密钥、ssh-agent、公钥配置Please tell me who you are...未配置用户名和邮箱执行git config --global user.name和user.emailThe following untracked files would be overwritten切换分支或合并时本地文件会和目标分支冲突备份未跟踪文件或确认是否需要后再切换Merge conflict in ...两个分支修改了同一文件的同一位置git status查看冲突文件手动解决并重新 add/commitfailed to push some refs远程有新提交本地落后先git pull或git fetch并解决冲突再 pushYou have unstaged changes切分支时工作区不干净先提交或 stash 当前改动HEAD is now at ...之后找不到提交误执行了reset --hard用git reflog找到记录并恢复仓库里全是node_modules之类的文件缺少.gitignore创建.gitignore并忽略依赖、构建产物、IDE 配置等目录这里面每一项我都实际踩过至少一次。印象最深的是有一次在同事的仓库里执行git pull结果因为本地有未提交的改动拉取直接提示可能被覆盖。当时我用了git stash把现场存起来pull 完再git stash pop五分钟解决了问题。从那以后git status成了我每次操作前必看的命令。6.2 我给初学者的三条学习建议学 Git 命令有一个误区就是试图通过背选项来“掌握它”。实际上你不需要记住所有参数只需要记住三件事第一当前仓库处于什么状态第二我要把改动从哪个区域移动到哪个区域第三如果出问题了哪种操作能最小化损失地恢复。遇到任何不确定的情况先执行git status。它会明确告诉你当前在哪个分支、暂存区有什么、工作区有什么这些信息几乎能指导你完成 90% 的日常操作。剩下的 10%比如解决冲突时需要取舍两边代码靠的是对业务本身的理解而不是命令语法。第二个建议是刻意练习“小步提交”。每次提交只包含一个清晰的改动主题提交信息写成一句能看懂的话。这样做的好处是将来用git log和git diff定位问题时你能快速从历史里找到某次具体的改动而不是在一大坨“update”里翻找。第三个建议是找一个真实项目反复练手。我建议把个人博客、笔记库或者任何你正在写的项目纳入 Git 管理故意去试几次分支合并、冲突解决、reset 和 stash。只有亲手体验过“改坏了能找回”和“没保存就没了”的差别你才能真正理解 Git 那些命令为什么要这么设计。最后分享一个我自己的习惯我在每次做完一个阶段性改动后习惯用git log --oneline -5快速确认最近的提交状态。这个命令很短但已经成了我的条件反射。Git 的学习曲线确实有点陡但一旦把核心模型吃透它带给你的安全感和效率提升是任何图形化工具都替代不了的。希望这篇围绕 15 个核心命令的梳理能帮你少走一些我当年走过的弯路。
返回列表