简介围绕Git命令行操作核心场景整理了一份入门级速查资料面向Git初学者和日常参与协作开发的程序员解决版本控制中从环境配置到分支管理的常见问题。内容系统覆盖SSH密钥生成与验证、仓库克隆与拉取更新、代码提交与历史查看、工作区还原与版本回退、分支创建合并及删除、远程分支同步、冲突手动处理、标签发布以及stash和rebase等进阶命令结构化梳理了命令语法、适用时机与易错点。压缩包内共1个docx文档整体大小约349KB便于离线查阅目前已有378人学习下载。文档不是简单罗列命令而是按实际开发流程组织突出常见故障与解决办法例如未推送时如何撤销提交、如何强制删除未合并分支、如何同步本地与远程分支等适合开发者对照操作也可作为团队新人培训或日常备查的Git命令手册。1. GIT基本命令先搞懂命令背后的三个区域再谈背命令GIT基本命令看起来是最容易背的一类命令真到用时却最容易翻车你照着网上的清单敲了git add、git commit、git push一整天都在报错旁边同事看了一眼只问了一句“你切分支了没有”然后帮你敲了git checkout。这不是你不努力而是GIT的命令大多只告诉你“做了什么”没告诉你“对象是谁”——你对工作区、暂存区、仓库区三个区域没有画面命令背得再多也拼不到一条完整的工作流上。这篇笔记想做的就是把这个画面补上从为什么GIT要设计三个区域讲起再按“本地提交、历史回顾、分支合并、远程协作、故障恢复”这条真实工作流把每个环节真正高频的几个命令讲透包括参数、输出含义和可落地的排查办法。适合刚开始接触版本控制的开发者也适合团队里需要自己先理清基本操作再约束协作规范的负责人。2. GIT基本命令的地基快照模型与三区流转想理解GIT先接受一个反直觉的前提它保存的不是文件差异而是每一次提交时项目的完整快照。把这个前提吃透后面所有命令的“为什么”都会自动对上。2.1 为什么说GIT的提交是快照而不是补丁很多人拿GIT和SVN类比觉得版本控制就是把上一版和这一版之间的差异存起来。GIT恰恰相反。每次commitGIT把当前所有被跟踪文件的内容完整写入对象库然后记录一棵目录树的快照。文件没变的它会把指针指向上一个版本的对象实现空间复用文件变了就多出一个新blob对象。这套设计的直接后果是提交操作完全不依赖网络和服务器本地仓库包含全部历史diff只是事后在对象之间算出来的而不是预先存储的。这正是GIT基本命令里每个命令都显得“轻”的根本原因。像git checkout、git rebase这类命令本质上是在本地对象库中移动指针、重放指针不需要和远端反复通信。理解这一点你就能想明白为什么项目仓库有时候会膨胀严重为什么有时候切分支奇快为什么把某个文件的历史彻底清理干净很难——因为提交对象一旦生成就是固定的修改历史等于修改整张对象图。快照模型带来的第一个实操习惯是大多数项目里仓库体积失控都不是你的代码太多而是有人把node_modules、构建产物、安装包提交了进来。对象库会保留每一个出现过的blob你写一万行代码可能也就几百KB但一个dist目录里几百个文件每提交一次就完整存一份仓库会以肉眼可见的速度变大。初学阶段不需要深挖git gc和packfile机制但至少要有这个意识代码贡献不会撑爆仓库反复提交的大目录才会。我建议在动手之前先把这张图记在心里objects存内容refs存分支和标签指针HEAD记录当前所处分支。GIT基本命令里其余所有命令无非是在这三个部分之间做移动、比较和引用。2.2 三个区域的流转status一眼看懂当前状态先明确三个区域分别是什么。工作区你打开编辑器直接看到的文件改动不记录git status会显示为Modified或Untracked。暂存区git add之后改动就暂存在这里存的是“下一次commit要包含哪些内容”的索引在仓库里对应.git/index文件。仓库区git commit之后对象库里生成的永久快照包含全部历史提交。很多人问为什么要有暂存区直接commit不行吗。暂存区让“提交”变成两步先用add挑选这次提交要包含哪些文件再用commit生成快照。这对大改动特别实用——你可以分多次add让每次提交保持单一职责也可以在git diff --staged确认无误后才真正落锤。如果没有暂存区commit就只有一种语义“提交所有改动”这在复杂项目里会让历史变成一锅粥。# 新建文件后先看看它现在属于哪个区域 echo # demo README.md git status # 输出第一段是 Untracked files说明文件只在工作区GIT 完全没跟踪它 # 用 add 把文件送入暂存区 git add README.md git status # 变成 Changes to be committed表示接下来 commit 会包含它 # 用 commit 生成快照 git commit -m docs: 为仓库补充说明文件 git status # 输出干净On branch ... nothing to commit # 再改一次 README.md用 diff 看工作区未暂存的改动 echo update README.md git diff # 这里显示的差异是“工作区 vs 暂存区”不是工作区 vs 上次提交git status是判断三区状态的唯一入口提交前必看git add后面要接具体路径不要总是无脑git add -A新建文件不经过add永远不可能出现在提交里。关于git diffGIT基本命令里最容易混淆的点就在这里默认的diff对比的是工作区和暂存区而不是和上次提交对比因为暂存区里有一份你上次add的内容。想做不同对比需要换参数。场景期望结果常用命令工作区改乱了想丢弃恢复成暂存区或仓库区版本git restore 旧环境用 git checkout --add后悔了想取消暂存改动留在工作区不进下次提交git restore --staged提交信息写错了想改写替换最近一次提交git commit --amendgit restore是较新版本的推荐写法老版本环境通常用git checkout -- file和git reset HEAD file。表格里的三个场景几乎对应新手日常操作里80%的“后悔”先把它们记熟后面遇到问题不容易慌。2.3 最小命令集一个人开发先从这几个命令开始不需要一开始就把几十个命令全背下来。一个人写项目时真正高频的只有六个init、status、add、commit、log、diff。把这六个用顺你已经能完成一个完整开发周期的版本管理。# 进入项目目录 cd /path/to/project # 初始化仓库会在当前目录生成 .git 目录 git init # 第一次提交前先把不该入库的文件排除掉 printf # 编译产物\n/dist/\n/node_modules/\n.DS_Store\n .gitignore # 添加所有想要跟踪的文件 git add . git status # 提交-m 后面跟单行信息 git commit -m feat: 初始化模拟项目X的仓库骨架 git log --oneline.gitignore一定要在第一次add之前写好。否则把node_modules提交进去之后再来删除历史里仍然保留着那一大包对象仓库体积已经回不去了。node_modules、.env、build目录、IDE配置都属于“一眼就知道不该入库”的典型.env尤其危险里面通常是密钥一旦提交再删除历史里仍然有——要在第一次提交前就阻止它进场。git status会在每个阶段提示你下一步能跑什么命令这种提示是GIT设计者故意留给你的“防呆装置”。养成提交前先看它的习惯比多背十个命令更有价值。git log --oneline用来验证提交真的被记录下来了单行输出能让你快速扫完整个提交序列。3. 提交与历史commit、log、diff 的正确用法提交是把代码变成“可回溯版本”的唯一方式历史命令则决定了你是否敢在三个月后去改一个老模块。这章把这三组命令的细节补齐。3.1 提交信息这样写三个月后自己也能看懂提交信息没有统一标准时团队历史会变得没法看。我见过有项目里有人提交“update”有人提交“解决了导出问题”还有人提交“wip”。三个月后回查问题git log基本帮不上忙。所以至少统一前缀格式feat、fix、docs、style、refactor、perf、test、chore。前缀让log扫视更快正文让“为什么改”可读。命令本身变化不大变的只是你写-m时的那块内容。我一般会把提交信息分成两段标题行控制在50个字符内正文写清楚为什么改而不是流水账式地写“改了xx”。GIT基本命令里只有git commit支持用多个-m一次性完成这两段。# 第一个 -m 作为标题第二个 -m 作为正文 git commit -m fix: 修正登录接口在跨时区场景下提前过期 \ -m 原实现把过期时间算成了绝对时间用户时区靠西时会提前失效改成比较 issuedAt 与当前时间的差值并补充对应测试用例。-m可以用多次GIT会把第一个参数作为summary之后的作为长描述。想看完整信息时用git log -1想看到作者和提交者时间用git log --prettyfuller。单行写完发现信息不够好想改最近一次提交用git commit --amend -m 新信息。注意amend在底层是生成一个新提交而不是修改旧对象所以千万不要对已经推送并被别人拉取的公共提交顺手用amend否则历史会分叉。3.2 git log不要只会看默认输出默认的git log输出不带格式提交多了根本看不下去。真正有用的log命令是把筛选参数组合起来用你可以在几秒钟内回答“这个文件什么时候被改过”“这个关键词是哪次提交引入的”这类问题。# 查看最近5条单行形式 git log --oneline -5 # 图形化展示提交与分支关系 git log --oneline --graph --all --decorate # 限定时间窗口 git log --since2024-01-01 --until2024-06-30 --oneline # 按提交信息里的关键词找 git log --all --oneline --grepfix # 只看某个文件的历史 git log --oneline -- README.md # 每次提交改了哪些文件的概况 git log --stat--oneline把完整哈希缩成短哈希适合做横向扫视--graph用字符把分支结构画出来适合检查某次合并是从哪条线合进来的--all把藏在其他分支和标签上的提交也搜出来常在“明明提交过却找不到”的排查里用--后面跟路径是GIT语法里常见的路径分隔符git log、git diff、git show都吃这一套。如果只想追踪某个文件的历史哪怕它被重命名过可以用git log --follow -- README.md。这些筛选组合配上下一小节的diff历史就不再是一行行静态文字而是可以按文件、按作者、按时间切片的问题定位工具。3.3 diff 和 show提交前后到底改了什么命令对比对象典型用途git diff工作区 vs 暂存区写完代码没add先自查git diff --staged暂存区 vs HEADadd后想确认将要提交的内容git diff 分支A 分支B两个分支末端差异review前全局看改动git show 提交号某个提交的完整变更回看一次提交的具体内容git show不加提交号时默认展示HEAD指向的提交HEAD在GIT基本命令里代表“当前分支最新一次提交”是一个不断移动的指针。下面是一些缩小diff范围的写法# 只看改了哪些文件名 git diff --name-only # 看增删行数概况 git diff --stat # 显示某个提交的具体内容 git show HEAD git show abc1234看diff输出时要习惯两件事a/和b/是GIT自己加上的虚拟路径前缀不是内容路径-行代表移除或变更前的状态行代表新增或变更后的结果。很多新手把diff输出当成“错误报告”其实它只是在客观描述两个版本之间的差异。觉得逐行看太累可以用git diff --word-diff按词粒度展示或者用git difftool拉起图形化对比工具。日常在命令行阶段学会把上下文和差异信息拆开读就够用了。4. 分支与合并从 git branch 到冲突处理现场分支是让多人并行、让单人多线尝试的核心机制。在GIT里它其实只是一个指针理解这点后分支操作就不再神秘。4.1 分支的本质不是目录拷贝是指针移动分支就是一个指向某个提交对象的可变指针。创建分支只添加一个refs引用不会复制任何文件HEAD记录“当前检出的分支是谁”。因此git branch创建新分支几乎不占空间切换快得让人怀疑GIT在工作。# 查看本地分支与当前分支 git branch # 基于当前HEAD创建新分支不切换过去 git branch feature/pay # 切换到已有分支 git switch feature/pay # 创建并切换 git switch -c feature/pay # 删除已合并的分支-d 会阻止删除未合并分支 git branch -d feature/paygit switch是较新版本为分支切换专门拆出的命令老环境用git checkout。这两者的关系常让人困惑checkout其实有两副面孔——切分支、恢复工作区文件switch只负责切分支语义更干净。我的建议是新版本优先用switch恢复文件交给git restore别让一个命令兼职太多事这也是GIT后来拆分命令的初衷。删除分支时如果报“not fully merged”说明分支上有没被合并进来的提交。这时候不要直接-D强删先确认那些提交真的不要了再决定是合进去还是彻底放弃。4.2 merge 与 rebase两种合并思路适用场景完全不同维度mergerebase历史形态保留分叉出现合并点线性像串行开发提交行为不改已有提交可能新增合并提交重写被移动分支的提交哈希适合面公共分支、团队协作本地清理、整理自己的开发分支merge是“把两个分支的最新历史并到一个点上”适合对外发布的分支因为它不会改动已经被别人拉取过的提交。rebase是把当前分支的一系列提交逐个搬到目标分支末端之后让自己的提交排成一条直线。它在表面上让历史更好看但代价是原有提交的哈希全部变了。GIT手册里会警告你“不要对已共享的提交使用rebase”这句警告翻译成人话就是公共分支上的提交一旦被同事拉取就不能再rebase和强推否则团队里每个人都会遭遇上百个重复提交的混乱。如果分支要发版或合回主线历史里的分叉点反而是真实记录保留merge节点更安全。如果是自己本地开发分支上攒了一堆零散提交想整理成一条清晰的线git rebase -i可以合并、重排提交。GIT基本命令阶段先把git pull --rebase用顺等你想整理本地提交时再打开-i也不迟。4.3 冲突处理不要慌GIT把冲突现场给你标出来了冲突没有想象中可怕。GIT把无法自动合并的部分原样摆在你面前你只需要做出选择。# 假设 dev 分支和 main 分支都改过 config.js git merge dev # 执行后看到 Unmerged paths用 status 列出冲突文件 git status冲突发生时打开冲突文件会看到这样的标记 HEAD export const RETRY_MAX 3; export const RETRY_MAX 5; dev HEAD到之间是当前分支的内容到 dev之间是dev分支的内容。GIT不会替你删标记需要人工决策后保留一份再把三行标记删掉。# 手工解决后把它标记为已解决 git add config.js # 完成合并提交 git commit正确步骤是先git status看哪些文件处于both modified再打开文件找冲突标记保留期望的版本删掉标记行然后git add最后git commit。解决冲突后别急着提交——git add只是告诉GIT“人工选择后的版本是对的”不代表diff已经符合预期。我一般会再打开一眼冲突文件确认没有残留的或然后才提交。如果改到一半发现思路错了git merge --abort可以回到合并前状态。注意这个命令只在冲突未解决时可退如果已经开始手动处理并且提交了合并就已经完成再想退出只能走reset。冲突不等于你输了冲突标记本身就是GIT在告诉你“两个版本我无法自动合并”需要人来裁决。5. GIT基本命令的避坑指南5个翻车现场与排查步骤GIT基本命令真正“贵”的经验全写在排错场景里。下面五个坑是几乎每个团队都踩过的每个都按“现象 → 原因 → 解决”讲清楚。5.1 误提交大文件仓库体积暴增clone越来越慢现象某开发者把300MB的设计稿或者安装包add并commit进仓库push时传了很久之后团队每个人clone全仓库都要拉这一大块对象速度肉眼可见地变慢就算后来把文件删了仓库体积也没有降下来。原因GIT存快照提交过一次的内容就会永远留在历史里。普通删除只影响新的提交老提交仍然持有那个blob对象。之后无论谁clone都要把全部历史对象拉下来包括那个早就“删掉”的大文件。解决从根上重写历史再用.gitignore防复发。常见做法是先在本地把无关改动收起然后用历史重写工具把该文件从全部提交里摘除最后再强推。# 1. 先把远程状态同步好避免本地提交干扰 git fetch origin # 2. 从全部历史中移除指定路径该工具需要单独安装 git filter-repo --invert-paths --path path/to/big-file.bin # 3. 确认历史里已经找不到该文件 git log --oneline --all -- path/to/big-file.bin--invert-paths的含义是“排除指定路径”等价于让该文件在所有历史提交里消失。命令执行后所有提交哈希都会改变因此整个团队需要在统一的时间点重新clone否则某个人手上的旧提交会重新把大文件推回远端。对还需要持续追踪大文件的场景应该用LFS这类扩展来管理而不是指望仓库能自行消化体积。5.2 reset --hard 把工作区改没了先学 reflog 再来谈“后悔药”现象想回退版本git reset --hard 旧提交一敲当前分支和工作区全部跳到旧状态刚写了几个小时的代码没有提交直接消失git status一片干净仿佛从未存在过。原因--hard同时重置了仓库区、暂存区、工作区三个区域。GIT把“放弃全部修改”做得非常彻底却不会提前问一句“真的要放弃吗”。这是GIT基本命令里最反直觉的一个设计。解决用git reflog找回历史。reflog是本地仓库的“操作日志”每次HEAD移动都会留一条记录。# 查看最近的 HEAD 移动记录 git reflog --oneline -10 # 输出大概是这样的格式 # abc1234 HEAD{0}: reset: moving to abc1234 # def9876 HEAD{1}: commit: feat: 完成结算页重构 # 回到 reset 之前的那个提交 git reset --hard def9876reflog里看到的“moving to”就是刚才把分支指针强制移过去的那一步只要记录还没被清理工作区状态就能找回来。reflog只是索引只要索引指向的提交对象没有被gc真正删除就能恢复。极少数情况下遇到“找不到记录”先确认是不是有人手动执行过git gc或git reflog expire。我见过有人用一次reset把刚写的营销页模板删没了靠reflog二十分钟捡回来了这个命令值得在第一次用--hard之前就记住。5.3 提交到了错误分支cherry-pick 和 stash 的正确打开方式现象在feature/alpha上写了一个下午发现本应放在feature/beta上提交或者已经落在错误分支或者还在工作区没commit。原因切分支前没看当前在哪条分支。这几乎是新手最常见的失误老手偶尔也会在多个分支并行时翻车。解决分两种情况。改动还没提交先stash切目标分支再pop。# 保存未提交改动 git stash push -m wip: 结算页未完成改动 # 切到目标分支 git switch feature/beta # 把改动恢复出来 git stash pop # 如果 pop 后出现冲突说明两个分支的代码基线不同手动处理即可改动已经提交用cherry-pick把提交复制过去再把它从原分支摘掉。# 记下当前分支最新提交的 hash git log --oneline -1 # 输出示例a3f22d1 feat: 完成结算页重构 # 切到目标分支并复制提交 git switch feature/beta git cherry-pick a3f22d1 # 回到原分支把这条提交从该分支移除 git switch feature/alpha git reset --hard HEAD~1cherry-pick会基于当前分支创建一个内容相同但哈希全新的提交git reset --hard HEAD~1用于从错误分支上移除这个提交前提是该提交从没被推送过远程。如果已经推上去了不能直接reset历史要走git revert生成反向提交否则会和其他人已拉取的历史打架。5.4 无脑 git pull 招来一堆冗余 merge 节点现象本地先做了几个提交几天没pull一跑git pull弹出一个编辑器界面写着“Merge branch ...”保存后历史里多了一个合并节点团队检查历史时主线从哪里开始岔的都看不清。原因git pull默认是fetch merge本地有分叉时自动走merge生成合并提交。合并提交本身合法但对单人的零散改动来说它是噪音——一次有效变更被埋在了无意义的合并点里。解决本地个人分支上常用git pull --rebase把本地提交重放到远端新提交之后。# 拉取远程更新并用 rebase 方式重放本地提交 git pull --rebase origin main # 如果 rebase 过程中出现冲突解决后继续 git add 冲突文件 git rebase --continue # 想整体回退到拉取前的状态 git rebase --abort--rebase的效果是先把本地提交暂存起来把分支指针快进到远端最新提交再逐个重放本地提交历史保持线性。中途冲突的解决方式和merge相似但变基进行到一半时用git rebase --abort可以整体回退到拉取前。注意这个操作同样不要用在已经推送到共享远端的分支上rebase会改写哈希后续push注定撞墙自己的个人开发分支则随便用。5.5 换行符导致整个文件都标红Windows 用户的玄学现场现象在Windows上用默认编辑器偶尔改一行代码git diff却显示整个文件几百行都被改动过有时候文件没动git status里却一直显示Modified。原因Windows默认使用CRLF行尾项目文本文件用的是LF。GIT把行尾差异当成内容差异于是每一行看起来都变过。这不是GIT算错了是它把换行符当成了内容的一部分。解决仓库根目录放.gitattributes统一行尾规则再按项目情况配置core.autocrlf。* textauto *.sh text eollf *.bat text eolcrlf# 对已经搞乱行尾的文件做一次归一化 git add --renormalize . # 查看归一化后还有没有差异 git status # 提交这次归一化 git commit -m chore: 统一仓库内换行符策略.gitattributes比全局配置可靠因为它随仓库走团队每个人共用一套规则。*.sh按Linux脚本习惯用LF*.bat保留CRLF其余文本文件交给textauto自动判断。.gitattributes提交后配合--renormalize一次性冲洗工作区里的行尾后续再跑diff就不会出现整个文件标红的玄学现场了。6. 把GIT基本命令用出效率alias、bisect 和提交前一条防线基本命令够用之后真正拉开效率差距的是几个常被忽略的辅助设施。这章给三个我每天都会用到的习惯。6.1 alias把那串高频命令压缩成两三个字母git config --global alias.co checkout git config --global alias.br branch git config --global alias.st status git config --global alias.lg log --oneline --graph --all --decorate git config --global alias.unstage restore --stagedalias.lg把第3章里那串log参数一次性固化输入从十几个字符降到两个字母。配置保存在~/.gitconfig跨机器同步时把这份配置文件带过去就行不用重新背一遍。6.2 bisect用二分查找定位第一个坏提交假设最近几周某个功能回归了你知道上个月还能跑但不清楚是哪次提交引入的。手工逐个checkout再跑测试很累git bisect可以把提交历史二分再配合测试脚本自动判定效率从O(n)变成O(log n)。# 启动二分 git bisect start git bisect bad # 当前 HEAD 是坏的 git bisect good v1.2 # 某个已知正常的历史标签 # 每次切换到一个中间提交后用脚本自动判定 git bisect run npm test # 脚本退出 0 代表 good非 0 代表 bad # 结束后定位到引入问题的提交 git bisect log # 退出 bisect回到原来的分支 git bisect reset这个命令不常用但一旦你在查一个持续几周的回归缺陷它就是最省事的一条路。6.3 pre-commit 钩子提交前自动拦截低级错误钩子默认放在.git/hooks目录不进版本管理。为了团队共享常见做法是放到项目.githooks目录并设置core.hooksPath。mkdir -p .githooks cat .githooks/pre-commit EOF #!/bin/sh # 检查将要提交的内容里是否有多余空白字符 git diff --cached --check if [ $? -ne 0 ]; then echo 提交被拦截检测到尾随空白请先修复 exit 1 fi EOF chmod x .githooks/pre-commit git config core.hooksPath .githooksgit diff --cached --check是GIT基本命令里很少被人注意却极其实用的组合它只检查即将提交的差异不会误报历史问题。钩子脚本内部任何非零退出码都会中止提交这套配置随仓库走新clone下来就是同一套规则。这些技巧看起来都不难真正难的是把“提交前看一眼status、回退前看一眼reflog”变成本能。我自己被坑过两次第一次是不看分支直接commit把改动写到了主线第二次是reset --hard之后想都不想就关终端结果临时存的思路彻底丢了。后来养成的习惯很朴素——add之前跑git statuscommit之前跑git diff --staged回退之前跑git reflog。基本命令就这么多能救你于水火的是执行顺序。希望帮到你。本文还有配套的精品资源点击获取