























备注:Deepseek帮助下整理的学习内容。
看到 Git 报错就慌,这几乎是每个开发者都会经历的阶段。别担心,这不完全是基础不牢,更多是因为 Git 设计了一个复杂的 “三区(工作区-暂存区-版本库)模型” ,我们常常对命令执行的“副作用”感到未知和失控。
要打破这种不确定性,关键在于先理解这个三区模型,把抽象的 Git 命令和文件实际的状态变化对应起来,然后再去反复练习那些最核心、最常用的指令。
下面是一套完整的 Git 学习路线,我们一起从底层原理开始,逐步构建自信。
git config --global user.name "你的GitHub用户名"
git config --global user.email "你的GitHub注册邮箱"
你可以用 git config --global --list 来查看所有配置。
ssh-keygen -t rsa -b 4096 -C "你的邮箱"
cat ~/.ssh/id_rsa.pub
将输出的公钥完整复制,然后添加到 GitHub 的 Settings → SSH and GPG keys → New SSH key 中。
Git 的核心工作流围绕着 工作区、暂存区、版本库 这三个区域展开,理解它们是避免混乱的基础。
git add 就是将工作区的修改放入暂存区,准备提交。git commit 会将暂存区的修改打包成一个永久快照(commit)保存到版本库,并生成一个唯一的 SHA-1 哈希值(例如 f3b7c5d)作为标识。现在,我们来创建一个新项目,把这些命令和概念串联起来。
graph TD subgraph 本地 A[工作区 Working Directory] -- "git add" --> B[暂存区 Staging Area]; B -- "git commit" --> C[本地仓库 Local Repo]; end C -- "git push" --> D[远程仓库 GitHub]; D -- "git pull" --> C;
mkdir git-practice
cd git-practice
git init # 初始化本地仓库
echo "# My Git Practice" > README.md
git status # 查看状态,文件标红表示未跟踪
git add README.md
git status # 文件变绿,表示已进入暂存区
git commit -m "first commit"
git remote add origin git@github.com:你的用户名/git-practice.git
git branch -M main # 强制修改本地分支名master为main。本地git默认建立的默认分支名是master,而github上默认是main。
git push -u origin main # 首次推送,-u 会建立关联。下次可以分支下直接用“git push”省略后面的参数推送。
关联后的分支,可以直接用简写命令git push推送。
查看本地分支是否和服务器分支的关联
git branch -vv
这样就代表没有关联

关联了会显示origin/xxx。

如何手动设置远程分支和本地分支的关联?
2种方法,在push的时候加-u
git push -u origin main(推荐)
或者
git branch --set-upstream-to=origin/远程名 本地名
取消本地分支和服务器分支的关联。
git branch --unset-upstream
如果本地分支名和github分支名不一样,每次推送需要带上本地名:远程名,不是很方便,所以尽量保证本地分支和服务器分支名一致:
#git push origin 本地名:远程名
git push origin master:main
git log --oneline # 简洁地查看提交历史
git diff # 查看工作区与暂存区的差异
git diff --staged # 查看暂存区与上次提交的差异
git show <commit-id> # 查看某次提交的具体改动
团队协作的本质,就是每个人在自己的分支上独立开发,最后通过合并请求(Pull Request / Merge Request)将功能安全地合入主分支。
分支提供了一个独立的环境,让你在开发新功能时完全不会干扰到 main(或 aster)分支上的稳定代码。这对于团队协作至关重要。
这是非常适合团队的标准模式。你可以在我们之前的项目 flask-ci-demo 中创建 dev 分支,然后开始练习:
git checkout -b dev # 从 main 分支创建并切换到 dev 分支
git push origin dev # 将 dev 分支推送到远程仓库
这个模型只有一条长期分支 main,所有开发都在短期分支上进行:
main 拉出新分支:git checkout -b feature/add-logingit push origin feature/add-loginmain 分支。git branch # 列出本地所有分支,当前分支前有 * 号
git branch -a # 列出本地和远程所有分支
git checkout main # 切换到 main 分支
git checkout -b feature-xyz # 创建并切换到新分支 feature-xyz
git branch -d feature-xyz # 删除本地分支 feature-xyz(需先切换出去)
git push origin --delete feature-xyz # 删除远程分支
冲突是 Git 协作中最常见的拦路虎,但只要理解它,解决起来并不难。
当两个人修改了同一文件的同一区域,Git 无法自动决定保留谁的,便会抛出冲突。处理步骤如下:
git merge 或 git pull 后,会看到类似 Automatic merge failed; fix conflicts 的提示。git status 查看哪些文件处于 both modified 状态。<<<<<<<, =======, >>>>>>> 标记。
<<<<<<< HEAD 到 ======= 之间是当前分支(接收方)的代码。======= 到 >>>>>>> branch-name 之间是另一分支(合并方)的代码。# 在执行git merge后出现冲突,你想撤销此次合并,回到自己合并前的代码就用下面的命令
git merge --abort
# 解决冲突后,标记为已解决并完成合并
git add <冲突文件>
git commit -m "Merge branch 'feature-branch' into main"
git checkout -b feature-a main
echo "Line from feature A" > test.txt
git add test.txt && git commit -m "Add file from A"
git checkout main
git checkout -b feature-b main
echo "Line from feature B" > test.txt
git add test.txt && git commit -m "Add file from B"
git checkout main
git merge feature-a # 第一次合并成功
git merge feature-b # 第二次合并时会产生冲突
test.txt,按规则修改,然后继续:git add test.txt
git commit -m "Merge feature-b and resolve conflict"
很多人在面对复杂历史时不敢操作,核心是没分清场景。牢记这个原则:如果代码已经推送到远程,优先用 git revert;如果仅在本地,可用 git reset。
| 使用场景 | 命令选择 | 原理 | 安全性 |
|---|---|---|---|
| 撤销本地修改(未 add) | git restore <file> |
用版本库的版本覆盖工作区 | 安全,无法恢复 |
| 撤销暂存区修改(已 add,未 commit) | git restore --staged <file> |
把文件从暂存区撤回工作区,修改保留 | 非常安全,文件不变 |
| 撤销本地提交(未 push) | git reset --soft HEAD~1 |
撤销 commit,但保留修改 | 需谨慎,历史已变 |
| 撤销已推送的提交 | git revert <commit-hash> |
创建一个新提交来抵消旧提交 | 最安全,推荐 |
git revert (最推荐)
原理: 通过创建一个新的 提交 来撤销某个历史提交的更改,是公共协作中最安全的操作。
用法:
# 先找到要撤销的 Commit ID
git log --oneline
# 使用 revert 创建一个反向提交来撤销它
git revert <commit-id>
# 推送到远程
git push origin main
git reset (需谨慎使用)
HEAD 指针到指定位置,历史会被修改。--soft:只撤销 git commit,修改保留在暂存区。--mixed(默认):撤销 git add 和 git commit,修改保留在工作区。--hard:危险! 完全丢弃所有修改,回到指定版本状态。用法:
# 撤销最近一次提交,保留修改在工作区
git reset --soft HEAD~1
# 撤销最近3次提交,并丢弃所有修改(非常危险!)
git reset --hard HEAD~3
# 如果已经推送,必须强制推送(严禁在公共分支上执行)
git push --force-with-lease
紧急救援:git reflog
当你用 reset --hard 误删了提交,git reflog 是你最后的救命稻草,它记录了所有 HEAD 的移动历史。
用法:
git reflog
# 假设找到目标 Commit ID 是 abc1234
git reset --hard abc1234
为了让你彻底掌握回滚与撤销,请在一个全新的、无风险的练习目录中按步骤操作。
步骤 1:创建一个新的 Git 仓库并模拟正常提交
cd ~/Desktop # 或任意安全目录
mkdir git-rollback-practice
cd git-rollback-practice
git init
# 创建文件并提交 v1
echo "Version 1" > app.txt
git add app.txt
git commit -m "v1: initial app.txt"
# 提交 v2
echo "Version 2" >> app.txt
git commit -am "v2: add version 2 line"
# 提交 v3(最终正确版本)
echo "Version 3" >> app.txt
git commit -am "v3: add version 3 line"
# 查看提交历史
git log --oneline
此时历史应该有三条提交,假设它们的 ID 分别为 1111111 (v1), 2222222 (v2), 3333333 (v3)。
步骤 2:模拟错误提交(不小心提交了错误内容)
echo "This is a BUG" >> app.txt
git commit -am "BUG: wrong change"
现在有四个提交,最新的那个是错误提交。
步骤 3:使用 git reset 撤销本地错误提交(未推送)
假设你还没有推送到远程,想撤销最后一次提交但保留修改在工作区:
git reset --soft HEAD~1
git status
你会看到 app.txt 仍处于“Changes to be committed”状态(即暂存区)。你可以再次修改 app.txt,然后重新提交正确的版本。
主要的应用场景:
git commit -m "新的注释",快速上传。git reset --soft HEAD~3,然后git commit 提交。git add 修改的文件,然后 git commit 快速上传。git reset HEAD~1的话,那么全部的修改都要add一遍,然后git commit。如果想完全丢弃最后一次提交的所有修改(危险):
git reset --hard HEAD~1
git log --oneline # 错误提交已经消失
步骤 4:模拟“已推送的错误提交”并使用 git revert 安全修复
# 重新制造一个错误提交并推送到远程(先添加一个远程仓库,例如 GitHub 上的空仓库)
git remote add origin git@github.com:你的用户名/git-rollback-practice.git
git branch -M main
git push -u origin main
# 现在制造一个错误提交
echo "Another bug" >> app.txt
git commit -am "BUG: another wrong change"
git push origin main
此时远程仓库已经包含了这个错误提交。要修复它,且不破坏公共历史:
# 找到错误提交的 commit ID
git log --oneline
# 使用 revert(假设错误提交 ID 是 a1b2c3d)
git revert a1b2c3d
# Git 会打开编辑器让你输入撤销的提交信息,按Esc,输入:wq保存并退出(其实是个vim)
git push origin main
现在查看远程仓库,你会发现多了一条“Revert”的提交,而历史中错误提交仍然存在,但已被逻辑撤销。这是团队协作中最安全的方式。
步骤 5:紧急救援——使用 git reflog 找回丢失的提交
假设你不小心执行了 git reset --hard HEAD~2,丢掉了两个提交。不要慌!
git reflog
你会看到一列 HEAD 移动记录,例如:
3333333 HEAD@{0}: reset: moving to HEAD~2
4444444 HEAD@{1}: commit: BUG: another wrong change
5555555 HEAD@{2}: commit: BUG: wrong change
...
找到你想要的提交 ID(例如 4444444),然后:
git reset --hard 4444444
丢失的提交就回来了。
我不要本地的新改动了,就让本地完全跟远程一样。
注意: 它会“丢弃”你本地未提交的修改和未推送的提交(如果有的话),所以别在有重要未提交改动时乱用。
# 1. 先把远程的最新信息拉下来
git fetch origin
# 2. 把本地 main 强行指到“跟 origin/main 一样”
git reset --hard origin/main
| 我想做的事... | 命令 |
|---|---|
| 场景一:修代码 | |
| 拉取最新代码 | git pull origin main |
| 把工作区的文件加入暂存区 | git add <file> (或 git add . 添加所有) |
| 把暂存区的修改提交到本地仓库 | git commit -m "feat: 添加了xxx功能" |
| 推送本地提交到远程仓库 | git push origin main |
| 场景二:代码冲突 | |
| 合并时遇到冲突,想直接放弃合并 | git merge --abort |
| 解决冲突后,标记为已解决并完成合并 | git add <file> → git commit |
| 场景三:撤销修改 | |
| 放弃工作区所有未暂存的修改 | git restore . |
| 撤销暂存区的所有文件 | git restore --staged . |
| 场景四:版本回滚 | |
| 安全撤销已推送的提交 | git log --oneline (查ID) → git revert <commit-id> → git push |
| 撤销本地最近3次提交,但保留修改 | git reset --soft HEAD~3 |
| 紧急恢复误删的提交 | git reflog (找ID) → git reset --hard <commit-id> |
| 分类 | 命令 | 作用 |
|---|---|---|
| 初始化与配置 | git init |
在当前目录创建一个新的 Git 仓库。 |
git clone <url> |
克隆一个远程仓库到本地。 | |
git config --global user.name "Your Name" |
设置全局用户名。 | |
| 基础操作 | git status |
查看工作区和暂存区的状态。 |
git add <file> |
将指定文件加入暂存区。 | |
git commit -m "message" |
将暂存区的改动提交,并附上信息。 | |
git push origin <branch> |
将本地分支推送到远程仓库。 | |
git pull origin <branch> |
拉取远程分支的更新并与本地合并。 | |
| 分支与合并 | git branch |
列出本地所有分支。 |
git checkout -b <branch> |
创建并切换到一个新分支。 | |
git merge <branch> |
将指定分支合并到当前分支。 | |
| 撤销与回滚 | git restore <file> |
丢弃工作区的修改。 |
git reset HEAD~1 |
撤销最近一次提交,改动回到工作区。 | |
git revert <commit-id> |
安全地撤销一个已推送的提交。 | |
| 查看历史 | git log --oneline |
以一行的形式查看简洁的提交历史。 |
git diff |
查看工作区与暂存区的差异。 | |
git reflog |
查看 HEAD 的移动历史,用于找回丢失的提交。 |
学好 Git,核心在于完成从“死记命令”到“理解流程”的转变。
git revert 用于远程,git reset 用于本地 的原则。万一误操作,git reflog 是你最后的救星。此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。