认识工作区、暂存区和版本库
- 理解下 Git 工作区、暂存区和版本库概念
- 工作区:就是你在电脑里能看到的目录;
- 暂存区:英文叫 stage 或 index,一般存放在 .git 目录下的 index 文件 (.git/index) 中,所以把暂存区有时也叫作索引 (index);
- 版本库:工作区有一个隐藏目录 .git,这个不算工作区,而是 Git 的版本库;
- git 操作
Git 安装 + 完整配置
-
全平台安装
- Windows:Git 官网 下载,一路默认;
- Mac:
brew install git; - Linux:
sudo apt-get install git;
-
全局 / 本地 / 系统三级配置:
# 1. 全局配置(所有仓库生效,第一次必配) git config --global user.name "你的名字" git config --global user.email "你的邮箱" git config --global core.editor "code --wait" # 默认编辑器 VS Code git config --global core.autocrlf true # Windows 换行符 git config --global core.autocrlf input # Mac/Linux 换行符 # 2. 本地配置(仅当前仓库生效,优先级最高) git config --local user.name "项目专用名" # 3. 查看所有配置 git config --list --show-origin -
SSH 免密配置 (告别每次输密码)
# 1. 生成 SSH 密钥 ssh-keygen -t ed25519 -C "你的邮箱" # 2. 复制公钥 # Windows:clip < ~/.ssh/id_ed25519.pub # Mac:pbcopy < ~/.ssh/id_ed25519.pub # 3. 粘贴到 GitHub/GitLab -> Settings -> SSH keys # 4. 测试 ssh -T git@github.com
Git 完整命令手册
仓库初始化与克隆
# 初始化本地仓库(生成 .git 文件夹)
git init
# 克隆远程仓库(完整复制所有版本历史)
git clone 仓库地址
# 浅克隆(只克隆最新版本,体积小)
git clone --depth=1 仓库地址
文件操作 (工作区 → 暂存区 → 本地仓库)
# 查看文件状态(最常用,红色=工作区,绿色=暂存区)
git status
# 查看修改内容(工作区 vs 暂存区)
git diff
# 查看暂存区 vs 本地仓库
git diff --staged
# 添加到暂存区
git add 文件名 # 单个文件
git add 文件夹/* # 文件夹
git add . # 所有修改(推荐)
git add -u # 仅更新已跟踪文件
# 提交到本地仓库
git commit -m "提交说明" # 标准提交
git commit -am "说明" # 跳过 add,直接提交已跟踪文件
git commit --amend -m "新说明" # 修改上一次提交(未推送)
git commit --amend --no-edit # 补充修改到上一次提交,不改说明
# 查看提交历史
git log # 完整历史
git log --oneline # 简洁版
git log --graph # 图形化分支
git log --stat # 查看文件修改统计
git reflog # 查看所有操作记录(找回丢失提交)
远程仓库操作 (本地 ↔ 云端)
# 查看远程仓库
git remote -v
# 关联远程仓库
git remote add origin 仓库地址
# 修改远程仓库地址
git remote set-url origin 新地址
# 拉取远程代码(推荐,不会自动合并)
git fetch origin 分支名
# 拉取并合并(等于 fetch + merge)
git pull origin 分支名
# 推送到远程
git push origin 分支名
git push -u origin main # 第一次推送,绑定上游分支
git push -f # 强制推送(谨慎用!)
分支操作 (Git 最强大功能)
# 查看分支
git branch # 本地分支
git branch -r # 远程分支
git branch -a # 所有分支
# 创建分支
git branch 分支名
# 切换分支
git checkout 分支名 # 旧命令
git switch 分支名 # 新命令(推荐)
# 创建并切换分支
git checkout -b 分支名
git switch -c 分支名
# 重命名分支
git branch -m 旧名 新名
# 删除分支
git branch -d 分支名 # 安全删除(已合并)
git branch -D 分支名 # 强制删除(未合并)
# 推送本地分支到远程
git push origin 分支名
# 删除远程分支
git push origin --delete 分支名
标签 (发布版本用)
# 查看标签
git tag
# 创建轻量标签
git tag v1.0
# 创建附注标签(推荐)
git tag -a v1.0 -m "版本说明"
# 推送标签到远程
git push origin v1.0
git push origin --tags # 推送所有标签
# 删除标签
git tag -d v1.0
git push origin --delete tag v1.0
Git 冲突完全解决方案
-
冲突产生原因:多人修改同一文件同一行,或一人删除文件、一人修改文件,Git 无法自动合并;
-
冲突标记格式:
<<<<<<< HEAD # 当前分支代码 你的代码 ======= # 分隔线 别人的代码 >>>>>>> 分支名 # 合并分支代码 -
解决步骤:
- 打开冲突文件,手动删除标记,保留正确代码;
git add .标记冲突已解决;git commit -m "解决冲突"完成合并;git push推送;
-
冲突中止 (不想解决了)
git merge --abort git rebase --abort
企业级 Git 工作流
-
Git Flow (大型项目,最严谨)
- main:生产环境,永远稳定;
- develop:开发主分支;
- feature/*:功能开发分支;
- release/*:预发布分支;
- hotfix/*:线上紧急 bug 修复;
-
GitHub Flow (中小型项目,简单高效)
- 只有 main 主分支 + 功能分支;
- 功能完成 → 提 PR → 代码审查 → 合并到 main → 自动部署;
-
GitLab Flow (最佳实践,兼顾灵活与安全)
- 分支对应环境:development → test → production;
- 严格上游分支合并,避免混乱;
进阶操作
git reset 操作
-
原理:
git reset是 Git 中回退版本、撤销提交最核心的命令,简单说:它能把当前分支的 HEAD 指针,移动到指定的提交节点,同时控制工作区、暂存区的文件状态;git reset --hard 目标版本=> 回到目标版本,把目标版本之后的一切全部删掉;
-
git reset三种模式:模式 命令写法 作用 工作区 暂存区 软回退 –soft 仅移动 HEAD,不改动文件 保留修改 保留修改 默认模式 –mixed 移动 HEAD + 重置暂存区 保留修改 清空修改 硬回退 –hard 彻底重置,删除所有修改 清空修改 清空修改 -
高频实用场景:
- 场景 1:回退到当前分支的最新提交
- 清空工作区所有未提交的修改
- 清空暂存区所有
git add的内容 - 不改变版本历史 (不删除任何 commit)
git reset --hard # 等价于 git reset --hard HEAD- 场景 2:撤销最后一次提交 (重新提交)
git reset --soft HEAD~1 # 修改文件后重新提交 git commit -m "正确的提交信息"- 场景 3:撤销
git add(取消暂存)
# 等价于 git reset --mixed (默认模式) git reset- 场景 4:放弃所有本地修改,回到远程最新版
# 先拉取远程最新代码 git fetch origin # 硬回退到远程主分支 git reset --hard origin/main- 场景 5:回退后后悔了,想恢复版本 (git reset 不是 “不可逆”!用 git reflog 找回丢失的提交)
# 查看所有操作历史(包含被删除的提交) git reflog # 找到误删前的哈希值,硬回退回去 git reset --hard 丢失的哈希值 - 场景 1:回退到当前分支的最新提交
-
注意:
- ⚠️ 绝对不要在公共分支用
git reset --hard,多人协作,回退版本会覆盖别人的代码,导致冲突灾难,请使用git revert; - ⚠️ –hard 会永久删除未提交的修改,未提交的文件、
git add过的文件,都会被直接删除,无法通过普通方式恢复; - ⚠️ 回退后,远程分支需要强制推送 (仅在私人分支使用);
- ⚠️ 软回退 / 默认回退,文件不会丢,都是安全操作,放心用;
- ⚠️ HEAD~1 等价于 HEAD^ (回退 1 个版本:HEAD~1 = HEAD^,回退 2 个版本:HEAD~2)
- ⚠️ 绝对不要在公共分支用
git revert 操作
-
原理:
git revert是 Git 中安全撤销历史提交的核心命令,它不会删除 / 改写已提交的历史记录,而是通过新建一个 「反向提交」,抵消掉目标提交的所有修改,完美保留项目完整提交历史;- 简单理解:你提交了一个错误代码 (commit A),
git revert A会生成一个新提交 (commit A’),让代码回到 A 之前的状态,A 和 A’ 都会保留在历史里;
-
高频使用案例:
- 场景 1:撤销「最近一次提交」
# 提交记录 e4f5g6h -> a1b2c3d (HEAD -> main) git revert a1b2c3d # 代码自动回到 e4f5g6h 的状态!- 场景 2:撤销「历史中某一个旧提交」 (3 天前的一个提交引入了 bug,后面又提交了 5 次,只想撤销那一个错误提交)
# 直接指定那个旧的 commit 哈希即可 git revert 8f9a7b1- 场景 3:撤销「连续的多个提交」
# # 撤销连续的多个提交(左开右闭:不包含 start,包含 end) git revert 3d4e5f6..7a8b9c0- 场景 4:撤销「合并提交(merge)」 (把 dev 分支合并到了 main,发现有问题,要撤销这次合并)
# 找到 merge 提交哈希,必须加 -m 1(1 代表保留主分支,丢弃合并进来的代码) git revert -m 1 9s2k7d2- 场景 5:撤销后不自动提交 (可以手动修改代码后再提交)
git revert -n a1b2c3d # 手动修改代码 git add . git commit -m "手动撤销错误提交"- 场景 6:撤销错了,恢复回去 (revert 你的 revert 提交)
# 提交记录:a1b2c34 -> 9999999 Revert "a1b2c32" ← 这就是要 revert 的目标 git revert 9999999- 场景 7:冲突了,继续撤销/不继续撤销 (要撤销的旧代码,和后来新写的代码重叠了,Git 不知道怎么自动处理)
┌─────────────────────────────────┐ │ Git Revert 冲突处理图 │ ├─────────────────────────────────┤ │ 1. 看到 CONFLICT → 打开文件 │ │ 2. 删除 <<<<<<< ======= >>>>>>> │ │ 3. git add . │ │ 4. git revert --continue │ │ 5. 保存提交 → 完成 │ ├─────────────────────────────────┤ │ 放弃撤销:git revert --abort │ └─────────────────────────────────┘ -
注意:
- 不要用 revert 撤销根提交(第一次提交),因为没有父提交了,无法生成反向提交;
- 整个项目会被清空;
- 冲突爆炸,极难修复;
- revert 后不能再把原来的提交 cherry-pick 回来,想恢复必须:
- 要么 revert 那个撤销提交;
- 要么用
git cherry-pick -f强制;
- 不要用 revert 撤销根提交(第一次提交),因为没有父提交了,无法生成反向提交;
git rebase 操作
-
git rebase是 Git 中重构提交历史的核心命令,核心作用是:把一段提交记录「剪切」下来,「粘贴」到另一个分支的最新提交后面,让分支历史变成一条干净、线性、无分叉的直线,变基 = 给你的功能分支换一个 “最新的地基”,对于其他分支是没有改动的;- merge:保留所有分支历史,生成新的「合并提交」,历史会分叉;
- rebase:重写提交历史,抹平分支分叉,历史是一条直线;
-
常用使用场景:
场景 1:把主干最新代码同步到自己的功能分支 (最常用)- 你的功能分支开发时,主干 (main/master) 有别人更新的代码,你要把最新代码同步过来,同时保持历史干净、无线性分叉,初始状态如下;
main: A -- B -- C (最新) \ feature: D -- E -- F (你的功能分支) git checkout feature切换到你的功能分支 feature,要对 feature 分支变基,不能在 main 上操作;git fetch origin拉取远程主干最新代码,不执行这步,你变基的是旧版 main,同步无效 (git fetch origin 会把远程所有分支的最新代码下载到本地,不合并、不冲突);git rebase origin/main执行变基 (核心命令),这一步 Git 内部做了 4 件事;1. 找到 feature 和 main 的公共祖先 B 2. 把 feature 上 B 之后的提交 D E F 暂存 3. 把 feature 指向 origin/main 的最新位置 C 4. 把 D E F 一个个重新应用到 C 后面 main: A -- B -- C \ feature: D' -- E' -- F'- 如果发生冲突 (一定会遇到),Git 会暂停变基,并提示:
Auto-merging xxx.js CONFLICT (content): Merge conflict in xxx.js error: could not apply... - 打开文件,手动解决冲突;
- 冲突解决后,继续变基,❌ 绝对不要执行
git commit!否则会破坏变基流程;git add . # 只 add,不要 commit! git rebase --continue # 继续执行剩下的提交 # 如果想放弃变基,恢复到开始之前 git rebase --abort - 变基完成后推送到远程,因为 feature 历史被改写,必须强制推送,自己的功能分支放心
push -f,不会影响别人 (main);git push -f origin feature
- 你的功能分支开发时,主干 (main/master) 有别人更新的代码,你要把最新代码同步过来,同时保持历史干净、无线性分叉,初始状态如下;
场景 2:合并本地多个零散提交 (美化历史)git log --oneline查看最近要合并的提交;f7a392d 调试按钮 36bd283 修改样式 1a2b3c6 完成登录功能git rebase -i HEAD~3执行交互式变基;- HEAD~3 = 合并最近 3 个提交;
- -i = interactive 交互式模式;
- 进入编辑界面(关键)
pick 1a2b3c 完成登录功能 pick 36bd28 修改样式 pick f7a392 调试按钮 - 把后面的提交改成
pick = 保留提交、s / squash = 合并进前一个提交;pick 1a2b3c 完成登录功能 s 36bd28 修改样式 s f7a392 调试按钮 - Vim 编辑器:esc → :wq 保存退出;
- 编辑最终的提交信息 (Git 会让你输入合并后的提交描述,删掉无用信息,写一句清晰的);
-
注意:
- 只在自己的功能分支用 rebase (公共分支被多人使用,rebase 会改写提交历史,其他人拉代码会出现大量冲突、提交丢失,直接导致协作崩溃);
- 永远不要对 main/master 等公共分支执行 rebase;
- 解决冲突时只 add 不 commit;
- 变基后必须 git push -f 强制推送;
git stash 操作
-
git stash(Git 储藏 / 暂存) 是 Git 最实用的「紧急救火」命令,核心作用:在不提交当前代码的情况下,临时保存工作区和暂存区的修改,让你的工作目录回到干净状态 (简单说:你写了一半的代码不想提交,但必须先切走处理别的事,用它就对了);- 保存对象:只保存工作区 (未提交的修改) + 暂存区 (git add 过的文件),不包含未跟踪的新文件 (比如新建的、从未 add 过的文件);
- 存储位置:修改会被打包成一个储藏栈 (stash stack),遵循后进先出规则,最新的储藏永远在最上面;
- 恢复方式:可以随时把储藏的代码恢复到当前分支,也能恢复到其他分支;
- 状态变化:执行后,git status 会显示工作目录干净 (working tree clean);
-
最常用核心命令:
命令 作用 git stash快速保存修改 (默认备注:WIP on 分支名…) git stash save "备注信息"带备注保存 (推荐,方便查找) git stash list查看所有储藏列表 git stash pop恢复最新的储藏,并删除该储藏记录 git stash apply恢复最新的储藏,保留该储藏记录 git stash drop删除最新的储藏记录 git stash clear清空所有储藏记录 (谨慎使用) -
完整实战案例:
- 场景 1:开发到一半,需要切分支修紧急 bug
# 1. 查看当前修改(工作区有未提交代码) git status # 2. 临时保存代码,带备注方便识别 git stash save "开发用户登录功能-写到一半" # 3. 此时工作区变干净,可以放心切分支 git checkout main # 4. 修完bug,切回原分支 git checkout dev # 5. 恢复刚才的代码(自动删除储藏记录) git stash pop- 场景 2:保存多个储藏,指定恢复某一个
# 1. 查看所有储藏(stash@{0}是最新的) git stash list # 输出示例: # stash@{0}: On dev: 修复登录bug # stash@{1}: On dev: 开发用户列表功能 # 2. 恢复指定储藏(保留记录) git stash apply stash@{1} # 3. 删除指定储藏 git stash drop stash@{1}- 场景 3:保存新建的未跟踪文件 (默认不保存从未 git add 过的文件)
# 保存所有修改(包含未跟踪的新文件) git stash -u # 等价命令 git stash --include-untracked- 场景 4:只保存部分文件 (不想保存所有修改)
git stash push -m "只保存login.js" src/login.js- 场景 5:查看储藏的内容 (不恢复)
# 查看最新储藏的修改详情 git stash show -p # 查看指定储藏的详情 git stash show -p stash@{1} -
注意:
- 储藏不是提交,不要长期依赖;
- 储藏记录不会同步到远程仓库,换电脑就找不到了;
git stash clear会永久删除所有储藏,无法恢复!
git cherry-pick 操作
-
git cherry-pick是 Git 中精准提取某一个 / 几个提交 (commit),并复制到当前分支的命令 (不合并整个分支,只把指定的单次提交 “搬” 到当前分支); -
基础语法:
# 单个提交 git cherry-pick <commit-hash> # 多个离散提交 git cherry-pick <commit1> <commit2> <commit3> # 连续多个提交(左开右闭:包含commit2,不包含commit1) git cherry-pick <commit1>..<commit2> # 连续提交(包含起始和结束) git cherry-pick <commit1>^..<commit2> # 保留原提交的作者信息(推荐) git cherry-pick -x <commit-hash> -
常用案例:
- 场景 1:dev 分支修复 bug,同步到 master 上线;
# 切到 master git checkout master # picked dev 那个修复 commit git cherry-pick -x a1b2c3- 场景 2:feature 分支只想要某一个功能,不合并整个分支 (在 feature/user 写了很多功能,但只想把提交 9f8e7d 这个工具函数拿到 develop);
git checkout develop git cherry-pick 9f8e7d场景 3:不小心 reset 丢了提交,用 cherry-pick 找回 (误删了一个提交,幸好之前在另一个分支上 cherry-pick 过这个提交,现在可以从那个分支再 cherry-pick 一次回来);
- 场景 4:只应用代码,不自动提交 (手动修改后再提交)
git cherry-pick -n a1b2c3 # 修改完后: git add . git commit- 场景 5:picked 时顺便改 commit 信息
git cherry-pick -e a1b2c3- 场景 6:冲突时的标准处理案例
# 手动改文件解决冲突 git add . # 继续 cherry-pick 流程 git cherry-pick --continue # 想放弃这次 cherry-pick git cherry-pick --abort- 场景 7:picked 一个 merge 提交 (合并节点),普通 cherry-pick 不能直接 picked merge 是因为 Git 不知道你要复制哪条分支的代码;
# -m 1:代表主干分支(被合并到的那个分支) # -m 2:代表被合并进来的分支 git cherry-pick -m 1 8a7s6d -
注意:
- 不要在公共分支上随意使用,cherry-pick 会生成新提交,如果在多人共用的分支上操作,会导致提交历史混乱;
- 不要重复搬运同一个提交,同一个提交如果被搬运两次,会出现重复代码 / 冲突,可以用
git branch --contains <commit>查看该提交已经在哪些分支存在; - 推荐加 -x 参数,加 -x 会在提交信息中记录 (cherry picked from commit a1b2c3);
- 不要用它替代正常合并,cherry-pick 只适合小提交、小修复;
- 搬运前确保工作区干净,操作前最好
git stash或git commit,避免代码混乱;
面试题
git 与 svn 的区别在哪里?
git 和 svn 最大的区别在于 git 是分布式的,而 svn 是集中式的。因此我们不能再离线的情况下使用 svn,如果服务器出现问题,我们就没有办法使用 svn 来提交我们的代码。
svn 中的分支是整个版本库的复制的一份完整目录,而 git 的分支是指针指向某次提交,因此 git 的分支创建更加开销更小并且分支上的变化不会影响到其他人,svn 的分支变化会影响到所有的人;
svn 的指令相对于 git 来说要简单一些,比 git 更容易上手;
git pull 和 git fetch 的区别
git fetch 只是将远程仓库的变化下载下来,并没有和本地分支合并;
git pull 会将远程仓库的变化下载下来,并和当前分支合并;
git rebase 和 git merge 的区别
git merge 和 git rebase 都是用于分支合并,关键在 commit 记录的处理上不同;
git merge 会新建一个新的 commit 对象,然后两个分支以前的 commit 记录都指向这个新 commit 记录。这种方法会保留之前每个分支的 commit 历史;
git rebase 会先找到两个分支的第一个共同的 commit 祖先记录,然后将提取当前分支这之后的所有 commit 记录,然后将这个 commit 记录添加到目标分支的最新提交后面;经过这个合并后,两个分支合并后的 commit 记录就变为了线性的记录了;
🏷 音频和视频
上一篇