Git 常用命令与实战指南
Git 命令很多,但日常开发真正高频的并没有那么多。
这篇文章不追求把所有 Git 命令都罗列一遍,而是按照实际开发场景整理:
- 本地仓库与远程仓库配置
- 日常提交与暂存
add -i交互式暂存- 分支管理
merge/rebasecherry-pick- 修改、整理和合并 Commit
stashreset/revert- 远程仓库地址修改
- Tag
- 恢复误删分支
- 常见问题与 Git 工作流
如果刚开始学习 Git,推荐先通过 Learn Git Branching 可视化理解分支、提交、merge、rebase 等操作。
一、先理解 Git 的三个区域
很多 Git 命令之所以容易混淆,是因为没有先理解 Git 的数据流。
1 | 工作区 Working Tree |
对应几个最常用的动作:
1 | git add |
掌握 Git 的核心,不是记住几十条命令,而是知道:
现在代码在哪个区域?我想让它移动到哪里?
二、安装 Git
macOS + Homebrew
检查 Git:
1 | git --version |
检查 Homebrew:
1 | brew --version |
安装 Git:
1 | brew install git |
更新 Homebrew:
1 | brew update |
升级 Git:
1 | brew upgrade git |
Apple Silicon Mac 通常使用:
1 | /opt/homebrew/bin/git |
Intel Mac 通常使用:
1 | /usr/local/bin/git |
如果 which git 仍然指向:
1 | /usr/bin/git |
可以检查 PATH 是否正确。
例如 Apple Silicon:
1 | echo 'export PATH="/opt/homebrew/bin:$PATH"' >> ~/.zshrc |
其他安装方式可以参考:
三、Git 基础配置
3.1 配置用户名和邮箱
1 | git config --global user.name "your-name" |
查看配置:
1 | git config --global --list |
查看当前仓库最终生效的配置:
1 | git config --list --show-origin |
配置文件通常位于:
1 | ~/.gitconfig |
3.2 Git 配置的三个级别
Git 配置主要分为三个级别:
system
1 | git config --system |
通常对应:
1 | /etc/gitconfig |
影响整台机器。
global
1 | git config --global |
通常对应:
1 | ~/.gitconfig |
影响当前用户的所有 Git 仓库。
local
1 | git config --local |
对应当前仓库:
1 | .git/config |
只影响当前仓库。
优先级:
1 | system |
越靠下优先级越高。
3.3 删除用户配置
1 | git config --global --unset user.name |
如果只想修改当前项目:
1 | git config --local user.name "project-user" |
这对于一台电脑同时使用多个 Git 账号非常有用。
四、远程仓库 Remote 配置
查看远程仓库:
1 | git remote -v |
查看更详细的信息:
1 | git remote show origin |
添加远程仓库:
1 | git remote add origin git@github.com:user/project.git |
4.1 修改 origin 地址
这是非常实用的命令。
例如原来使用 HTTPS:
1 | https://github.com/user/project.git |
想改成 SSH:
1 | git remote set-url origin git@github.com:user/project.git |
查看是否修改成功:
1 | git remote -v |
完整流程:
1 | git remote -v |
如果远程仓库地址写错,也不需要删除再重新 remote add,直接:
1 | git remote set-url origin <new-url> |
即可。
4.2 修改 fetch URL
如果需要分别配置 fetch 和 push 地址,可以使用:
1 | git remote set-url origin <fetch-url> |
查看:
1 | git remote -v |
4.3 删除远程仓库
1 | git remote remove origin |
五、SSH 配置
生成 SSH Key:
1 | ssh-keygen -t ed25519 -C "your-email@example.com" |
如果环境不支持 ed25519,也可以使用 RSA:
1 | ssh-keygen -t rsa -C "your-email@example.com" |
查看公钥:
1 | cat ~/.ssh/id_ed25519.pub |
将公钥添加到 GitHub / GitLab 等平台。
测试 GitHub:
1 | ssh -T git@github.com |
如果公司网络无法访问 SSH 22 端口,可以考虑通过 SSH 443 端口。
编辑:
1 | vim ~/.ssh/config |
例如:
1 | Host github.com |
然后:
1 | ssh -T git@github.com |
六、创建仓库与第一次提交
6.1 初始化本地仓库
1 | git init |
查看状态:
1 | git status |
添加文件:
1 | git add . |
提交:
1 | git commit -m "feat: initial commit" |
关联远程仓库:
1 | git remote add origin <remote-url> |
推送:
1 | git push -u origin main |
-u 会建立本地分支与远程跟踪分支的关联。
之后可以直接:
1 | git push |
七、日常开发最常用命令
7.1 查看状态
1 | git status |
建议养成习惯:
不确定当前 Git 状态时,先
git status。
7.2 查看修改
查看工作区修改:
1 | git diff |
查看暂存区修改:
1 | git diff --staged |
或者:
1 | git diff --cached |
7.3 添加文件
添加指定文件:
1 | git add src/app.ts |
添加目录:
1 | git add src/ |
添加所有修改:
1 | git add . |
添加所有已跟踪文件的修改,同时包含删除:
1 | git add -A |
八、add -i:交互式暂存
git add -i 是非常值得掌握的命令。
当一个文件里同时包含:
1 | 修改 A:真正应该提交 |
但你又不想把整个文件全部 git add 时,可以使用:
1 | git add -i |
进入交互式界面。
常见选项包括:
1 | 1: status |
其中非常实用的是:
1 | patch |
也可以直接使用:
1 | git add -p |
它会逐块询问:
1 | Stage this hunk [y,n,q,a,d,s,e,?]? |
常见选择:
1 | y 暂存这一块 |
例如一个文件中:
1 | 修改登录逻辑 |
可以只把登录逻辑加入当前 Commit:
1 | git add -p |
这是保持 Commit 干净、提高 Code Review 质量非常重要的技巧。
九、Commit
9.1 创建 Commit
1 | git commit -m "feat: add login page" |
9.2 修改最近一次 Commit
修改 Commit message:
1 | git commit --amend -m "feat: add login page" |
如果只是补充遗漏的文件:
1 | git add . |
--no-edit 表示保留原来的 Commit message。
典型场景:
1 | git commit -m "feat: add login" |
注意:
如果这个 Commit 已经推送到公共分支,
amend会改变 Commit SHA,需要谨慎处理。
十、查看 Commit 历史
简单查看:
1 | git log |
单行查看:
1 | git log --oneline |
最近 10 条:
1 | git log -10 --oneline |
查看分支图:
1 | git log --oneline --graph --decorate --all |
推荐日常使用:
1 | git log --oneline --graph --decorate --all |
例如:
1 | * 8a12c4d (HEAD -> feature/login) fix: handle error |
Commit 后面的短字符串是 Commit 的标识符,可以使用它定位某一次提交。
十一、分支 Branch
查看本地分支:
1 | git branch |
查看所有分支:
1 | git branch -a |
创建分支:
1 | git branch feature/login |
创建并切换:
1 | git switch -c feature/login |
老版本 Git 也常见:
1 | git checkout -b feature/login |
切换分支:
1 | git switch feature/login |
删除本地分支:
1 | git branch -d feature/login |
强制删除:
1 | git branch -D feature/login |
删除远程分支:
1 | git push origin --delete feature/login |
十二、分支重命名
当前分支重命名:
1 | git branch -m new-name |
指定旧名称:
1 | git branch -m old-name new-name |
如果远程也需要同步:
1 | git push origin -u new-name |
十三、Merge
假设:
1 | main |
feature 开发完成后:
1 | git switch main |
Merge 的特点:
- 保留原有分支历史
- 可能产生 Merge Commit
- 不会修改已有 Commit
- 公共分支使用起来相对安全
查看 Merge 后的历史:
1 | git log --oneline --graph --decorate --all |
十四、Rebase
rebase 是 Git 中非常重要的命令。
假设:
1 | A---B---C main |
如果 feature 执行:
1 | git switch feature |
历史会变成类似:
1 | A---B---C main |
原来的 D、E 会被重新创建,所以 Commit SHA 会发生变化。
14.1 为什么使用 rebase?
主要目的是:
让自己的开发分支基于最新的主分支继续开发,同时保持一条比较干净的提交历史。
典型流程:
1 | git switch main |
如果发生冲突:
1 | # 解决冲突后 |
如果想取消整个 rebase:
1 | git rebase --abort |
如果想跳过当前 Commit:
1 | git rebase --skip |
14.2 rebase 注意事项
不要随意对已经被多人共享的公共分支进行 rebase。
因为:
1 | rebase = 重写历史 |
如果一个 Commit 已经被别人基于它开发,再重写这个 Commit,会给团队带来额外的同步成本。
简单记:
1 | 自己的 feature 分支: |
十五、Interactive Rebase:整理 Commit
开发过程中经常出现:
1 | feat: add login |
Code Review 前希望整理成:
1 | feat: add login |
可以使用:
1 | git rebase -i HEAD~6 |
Git 会打开类似:
1 | pick a111111 feat: add login |
常见操作:
1 | pick 保留 Commit |
例如:
1 | pick a111111 feat: add login |
最终可以整理成一个干净的:
1 | feat: add login |
15.1 fixup 的另一种用法
如果知道某个 Commit 是针对之前 Commit 的修复,可以:
1 | git commit --fixup <commit-id> |
例如:
1 | git commit --fixup a111111 |
然后:
1 | git rebase -i --autosquash HEAD~5 |
Git 会自动把 fixup Commit 放到对应 Commit 后面,并标记为:
1 | fixup |
这是非常推荐的 Commit 整理方式。
十六、Cherry-pick
git cherry-pick 用来:
把某一个已有 Commit 的修改复制到当前分支。
例如:
1 | main |
如果 C 需要把 feature 中的某一个修复 D 带到 main:
1 | git switch main |
结果类似:
1 | main |
注意:
D'不是原来的 D,而是一个新的 Commit,所以 SHA 会不同。
16.1 Cherry-pick 单个 Commit
1 | git cherry-pick <commit-id> |
16.2 Cherry-pick 多个 Commit
连续 Commit:
1 | git cherry-pick A^..C |
多个不连续 Commit:
1 | git cherry-pick A C E |
16.3 Cherry-pick 冲突
如果发生冲突:
1 | # 解决冲突 |
取消:
1 | git cherry-pick --abort |
跳过当前 Commit:
1 | git cherry-pick --skip |
16.4 Cherry-pick 常见场景
场景一:线上紧急修复
某个 Bug 修复已经在 feature 分支:
1 | feature |
线上 main 也需要这个 fix:
1 | git switch main |
场景二:把某个 Commit 移植到 release 分支
1 | git switch release/1.2 |
场景三:不同分支共享一个独立修复
如果某个 Commit 非常独立,cherry-pick 往往比完整 merge 一个分支更加合适。
十七、Merge / Rebase / Cherry-pick 怎么选?
这是实际开发中非常重要的区别。
| 命令 | 主要目的 | 是否重写历史 | 常见场景 |
|---|---|---|---|
merge |
合并整个分支 | 否 | 合并 feature 到 main |
rebase |
把自己的提交重新放到新基线之后 | 是 | feature 同步 main、整理历史 |
cherry-pick |
复制指定 Commit | 不修改原分支历史,但会产生新 Commit | 把某个 fix 带到另一个分支 |
可以简单理解:
1 | merge |
十八、Stash:临时保存代码
正在开发 feature:
1 | feature/login |
突然需要切到:
1 | feature/payment |
但是当前代码还没有完成,不适合 Commit。
可以:
1 | git stash push -m "WIP: login" |
切换分支:
1 | git switch feature/payment |
处理完成后:
1 | git switch feature/login |
查看 stash:
1 | git stash list |
恢复:
1 | git stash apply |
恢复指定 stash:
1 | git stash apply stash@{0} |
恢复并删除 stash:
1 | git stash pop |
删除指定 stash:
1 | git stash drop stash@{0} |
删除全部:
1 | git stash clear |
18.1 包含未跟踪文件
默认情况下,普通:
1 | git stash |
不会包含未跟踪文件。
如果需要:
1 | git stash push -u -m "WIP" |
其中:
1 | -u = --include-untracked |
十九、Reset
reset 更适合:
修改本地提交历史。
三种常见模式:
1 | git reset --soft <commit> |
区别:
| 模式 | Commit | 暂存区 | 工作区 |
|---|---|---|---|
--soft |
回退 | 保留 | 保留 |
--mixed |
回退 | 清除 | 保留 |
--hard |
回退 | 清除 | 清除 |
例如:
1 | git reset --soft HEAD~1 |
表示:
删除最近一次 Commit,但保留代码,并且代码仍然处于暂存状态。
常用于:
1 | 刚刚 commit 了 |
19.1 慎用 reset –hard
1 | git reset --hard HEAD~1 |
会丢弃对应的工作区修改。
如果代码没有其他备份,恢复会比较麻烦。
二十、Revert
revert 和 reset 不一样。
reset:
1 | 修改历史 |
revert:
1 | 创建一个新的 Commit |
例如:
1 | A---B---C |
执行:
1 | git revert C |
变成:
1 | A---B---C---D |
D 的内容用于抵消 C。
因此:
公共分支已经发布的 Commit,通常更适合使用 revert,而不是 reset。
20.1 Revert Merge Commit
如果需要撤销一个 Merge Commit:
1 | git revert -m 1 <merge-commit-id> |
-m 用于指定 mainline parent。
对于普通 Commit:
1 | git revert <commit-id> |
二十一、取消暂存
已经:
1 | git add . |
但是突然发现某个文件不应该提交。
取消暂存:
1 | git restore --staged <file> |
全部取消:
1 | git restore --staged . |
注意:
restore --staged只会把文件从暂存区移回工作区,不会删除你的代码修改。
二十二、丢弃工作区修改
如果确定不要某个文件当前的修改:
1 | git restore <file> |
全部丢弃:
1 | git restore . |
这个操作会丢失未提交的工作区修改,执行前确认。
二十三、恢复误删分支
如果误删了本地分支,不一定真的丢失。
先查看:
1 | git reflog |
找到被删除分支最近一次 Commit,例如:
1 | a1b2c3d |
重新创建:
1 | git switch -c feature/login a1b2c3d |
或者:
1 | git checkout -b feature/login a1b2c3d |
然后推送:
1 | git push -u origin feature/login |
二十四、Reflog
reflog 是 Git 排查“我刚才到底干了什么”的重要工具。
查看:
1 | git reflog |
例如:
1 | a1b2c3d HEAD@{0}: reset: moving to HEAD~2 |
如果:
1 | reset --hard |
或者:
1 | rebase |
之后发现代码不见了,可以优先考虑:
1 | git reflog |
找到之前的 Commit,再恢复。
二十五、Tag
Tag 常用于标记版本:
1 | v1.0.0 |
创建附注 Tag:
1 | git tag -a v1.0.0 -m "release v1.0.0" |
给指定 Commit 创建 Tag:
1 | git tag -a v1.0.0 <commit-id> -m "release v1.0.0" |
查看 Tag:
1 | git tag |
匹配:
1 | git tag -l "v1.*" |
推送:
1 | git push origin v1.0.0 |
推送所有 Tag:
1 | git push origin --tags |
删除本地 Tag:
1 | git tag -d v1.0.0 |
删除远程 Tag:
1 | git push origin --delete v1.0.0 |
二十六、获取远程仓库
克隆:
1 | git clone <remote-url> |
查看远程:
1 | git remote -v |
获取远程最新信息:
1 | git fetch origin |
查看远程分支:
1 | git branch -r |
查看所有分支:
1 | git branch -a |
二十七、Fetch / Pull / Push
fetch
1 | git fetch origin |
只获取远程更新,不直接修改当前工作分支。
推荐理解为:
1 | 远程仓库 |
pull
1 | git pull |
通常可以理解为:
1 | git fetch |
也可以使用:
1 | git pull --rebase |
表示获取远程更新后,通过 rebase 整理本地提交。
push
1 | git push |
推送当前分支。
第一次推送:
1 | git push -u origin feature/login |
二十八、强制推送
如果本地进行了:
1 | git rebase |
或者:
1 | git reset |
导致远程历史与本地历史不一致,可能需要强制推送。
推荐:
1 | git push --force-with-lease |
而不是直接:
1 | git push -f |
--force-with-lease 会进行额外检查,能够降低误覆盖别人最新提交的风险。
强制推送前一定确认当前分支是否有人共同开发。
二十九、文件名大小写
查看配置:
1 | git config --get core.ignorecase |
某些环境下可能需要:
1 | git config --local core.ignorecase false |
修改文件名大小写时,如果 Git 没有识别,可以通过中间名称:
1 | git mv App.tsx temp.tsx |
三十、删除 Git 暂存区中的所有文件
如果 .gitignore 后来增加了规则,但已经被 Git 跟踪的文件仍然存在于索引中,可以:
1 | git rm -r --cached . |
注意:
--cached只从 Git 索引中删除,不会删除本地文件。
因此不要轻易把:
1 | git rm -r -f . |
和:
1 | git rm -r --cached . |
混淆。
三十一、.gitignore
常见规则:
1 | # 忽略目录 |
例如前端项目:
1 | node_modules/ |
注意:
.gitignore主要针对尚未被 Git 跟踪的文件。如果文件已经提交过,仅仅增加.gitignore并不会让 Git 自动停止跟踪它。
可以:
1 | git rm --cached <file> |
然后提交。
三十二、文件名过长
Windows 某些场景可能遇到路径过长问题。
查看:
1 | git config --get core.longpaths |
必要时:
1 | git config --global core.longpaths true |
三十三、Git 常见工作流
33.1 Feature 开发
1 | git switch main |
33.2 Bug 修复
1 | git switch main |
33.3 线上紧急修复 + Cherry-pick
假设某个修复已经在 feature 分支:
1 | git switch feature/login |
找到:
1 | a1b2c3d fix: handle login error |
切换线上分支:
1 | git switch main |
Cherry-pick:
1 | git cherry-pick a1b2c3d |
如果测试通过:
1 | git push |
这样不需要把整个 feature 分支合并到 main。
三十四、一个比较推荐的日常习惯
1. 开始开发前
1 | git status |
2. 开发过程中
不要无脑:
1 | git add . |
可以多使用:
1 | git diff |
确保每个 Commit 都是相对完整的逻辑单元。
3. Commit
1 | git commit -m "feat: xxx" |
4. 同步主分支
自己的 feature 分支可以:
1 | git fetch origin |
5. 提交前整理历史
1 | git rebase -i HEAD~N |
或者:
1 | git rebase -i --autosquash HEAD~N |
6. 推送
普通情况下:
1 | git push |
如果刚刚 rebase 过自己的远程 feature 分支:
1 | git push --force-with-lease |
三十五、最容易混淆的几个命令
add
1 | git add |
把工作区修改放入暂存区。
commit
1 | git commit |
把暂存区内容创建成新的 Commit。
push
1 | git push |
把本地 Commit 推送到远程。
fetch
1 | git fetch |
获取远程最新信息,但不直接合并到当前分支。
pull
1 | git pull |
获取远程更新,并进行 merge 或 rebase。
merge
1 | git merge |
把另一个分支的历史合并进当前分支。
rebase
1 | git rebase |
把当前分支的 Commit 重新应用到新的基线之上。
cherry-pick
1 | git cherry-pick |
把指定 Commit 的修改复制到当前分支。
reset
1 | git reset |
调整当前分支指针,常用于修改本地历史。
revert
1 | git revert |
创建新的 Commit,抵消之前 Commit 的修改。
restore
1 | git restore |
恢复工作区文件,或者通过:
1 | git restore --staged |
取消暂存。
三十六、Git 命令速查表
| 场景 | 命令 |
|---|---|
| 查看状态 | git status |
| 查看修改 | git diff |
| 查看暂存修改 | git diff --staged |
| 暂存文件 | git add <file> |
| 交互式暂存 | git add -i |
| 按代码块暂存 | git add -p |
| 提交 | git commit -m "msg" |
| 修改最近提交 | git commit --amend |
| 查看历史 | git log --oneline |
| 查看分支图 | git log --oneline --graph --decorate --all |
| 创建分支 | git switch -c <branch> |
| 切换分支 | git switch <branch> |
| 删除分支 | git branch -d <branch> |
| 合并分支 | git merge <branch> |
| 变基 | git rebase <branch> |
| 交互式变基 | git rebase -i HEAD~N |
| 自动整理 fixup | git rebase -i --autosquash HEAD~N |
| 复制 Commit | git cherry-pick <commit> |
| 临时保存 | git stash push -m "msg" |
| 查看 stash | git stash list |
| 恢复 stash | git stash pop |
| 回退本地历史 | git reset |
| 撤销已提交修改 | git revert |
| 查看操作记录 | git reflog |
| 获取远程更新 | git fetch |
| 拉取更新 | git pull |
| 推送 | git push |
| 强制安全推送 | git push --force-with-lease |
| 查看远程地址 | git remote -v |
| 修改 origin | git remote set-url origin <url> |
| 添加远程 | git remote add origin <url> |
| 删除远程 | git remote remove origin |
| 创建 Tag | git tag -a <tag> -m "msg" |
| 推送 Tag | git push origin <tag> |
三十七、最终记住这张图
1 | ┌───────────────┐ |
而分支历史相关命令可以这样记:
1 | merge |
Git 真正值得掌握的,不是命令越多越好,而是形成下面这套思维:
1 | 先看状态 |