Git 必备命令指南:从日常高频到项目开发实战

Git 必备命令指南:从日常高频到项目开发实战

无论你是独自开发一个 side project,还是在十几人的团队里协作,Git 都是绕不开的工具。问题在于:Git 的命令多达上百条,真正天天用 的其实只有一小撮,而真正决定你能不能在项目里不翻车的,又是另外一小撮。

这篇文章把 Git 命令分成三层来讲:

  • 第一层:最基本常用命令 ------ 你每天会敲几十遍的那些,配场景。
  • 第二层:项目开发最重要的命令 ------ 分支、合并、回退、暂存、冲突处理,这些是团队协作里"会不会用 Git"的分水岭。
  • 第三层:典型团队工作流 ------ 把零散的命令拼成一套协作流程。

最后附一张速查表和几条避坑经验。读完它,你应对 95% 的日常开发场景应该都不用再查文档。


零、先做一次配置(一劳永逸)

装好 Git 之后,第一件事是告诉它你是谁。因为每一次提交都会带上你的姓名和邮箱,团队协作时这个信息会出现在 git log 和 GitHub/GitLab 的提交记录里。

bash 复制代码
git config --global user.name  "你的名字"
git config --global user.email "you@example.com"

几个强烈建议一并设置的项:

bash 复制代码
# 默认分支名改为 main(GitHub 已是默认)
git config --global init.defaultBranch main

# 让 git pull 默认用 rebase,历史更干净
git config --global pull.rebase true

# 让 push 默认只推当前分支
git config --global push.default current

# 彩色输出
git config --global color.ui auto

# 记住一次就免重复输入账号密码(HTTPS 场景)
git config --global credential.helper osxkeychain   # macOS
git config --global credential.helper cache         # Linux 临时缓存

查看所有配置:

bash 复制代码
git config --list

小贴士:去掉 --global 就是只对当前仓库生效(项目级覆盖全局级)。公司项目用一个邮箱、个人项目用另一个邮箱时很有用。


第一层:最基本常用命令(日常高频)

这一节的命令,你会用到肌肉记忆的程度。

1. 拿到代码:cloneinit

克隆一个已有仓库(最常见,从 GitHub 上拉项目):

bash 复制代码
git clone https://github.com/user/repo.git
# 只想克隆最近一次提交,省带宽
git clone --depth 1 https://github.com/user/repo.git

从零创建一个本地仓库

bash 复制代码
mkdir my-project && cd my-project
git init

2. 看清楚现在的状态:status

这是你最该频繁敲的命令,没有之一。它告诉你:改了哪些文件、哪些已暂存、哪些还没暂存、当前在哪个分支、是否领先/落后远程。

bash 复制代码
git status
# 简洁版
git status -s

3. 暂存与提交:add / commit

Git 的提交分两步:先把改动放到暂存区(staging area) ,再提交成一个快照。

bash 复制代码
# 暂存单个文件
git add README.md

# 暂存所有改动(包括新增、修改、删除)
git add -A
# 或等价的简写(当前目录及其子目录)
git add .

# 交互式选择要暂存的代码块(强烈推荐,写过的代码也能只挑一部分提交)
git add -p

提交:

bash 复制代码
git commit -m "修复登录页空指针崩溃"
# 想写多行、更规范的提交信息
git commit

一条好的提交信息长这样(Conventional Commits 约定):

scss 复制代码
fix(auth): 修复 token 过期后无法刷新的问题

清除定时器时漏掉了 refresh 调用,导致 401 后白屏。

4. 看历史与差异:log / diff

bash 复制代码
# 经典日志
git log
# 单行 + 图形化 + 显示分支(最常用)
git log --oneline --graph --all
# 只看最近 5 条
git log -5
# 看某个文件的修改历史
git log -- path/to/file.ts

未提交的改动

bash 复制代码
git diff              # 工作区 vs 暂存区(还没 add 的改动)
git diff --staged     # 暂存区 vs 上次提交(已 add、将要提交的改动)
git diff HEAD         # 工作区 vs 上次提交(所有未提交的改动)

5. 与远程同步:push / pull / fetch

bash 复制代码
# 把本地提交推到远程
git push
# 首次推送一个新分支到远程
git push -u origin feature/login

# 拉取远程更新并合并到当前分支
git pull
# 只把远程更新下载下来,不合并(更安全,可以自己决定怎么合)
git fetch

默认情况下 pull = fetch + merge;如果你像前面那样设置了 pull.rebase true,则变成 fetch + rebase,历史更干净。无论哪种,都养成"先 fetch/pullpush"的习惯,能避免大部分冲突。


第二层:项目开发最重要的命令

到这里往后,是区分"会用 Git"和"Git 用得好"的关键。

1. 分支管理:branch / switch / checkout

分支是 Git 的灵魂。团队开发的基本规则是:永远不要在 main 上直接写代码,每个功能开一个分支。

bash 复制代码
# 查看所有本地分支(* 是当前分支)
git branch
# 查看所有分支(含远程)
git branch -a

# 创建新分支(但不切换过去)
git branch feature/login

# 切换分支(Git 2.23+ 推荐用 switch,语义更清晰)
git switch feature/login
# 老写法,仍然能用
git checkout feature/login

# 一步到位:基于当前分支创建并切换
git switch -c feature/login
# 老写法
git checkout -b feature/login

# 删除已合并的分支
git branch -d feature/login
# 强制删除(即使没合并)
git branch -D feature/login

实战场景 :你想基于远程的 develop 分支开个新功能,而不是基于本地的 main

bash 复制代码
git fetch origin
git switch -c feature/payment origin/develop

2. 合并与变基:merge / rebase

把分支的代码并回主线,有两条路。

bash 复制代码
# 合并:把 feature 合进当前分支,会产生一个 merge commit
git switch main
git merge feature/login

# 变基:把 feature 上的提交"嫁接"到 main 最新位置之后,历史是一条直线
git switch feature/login
git rebase main

两者怎么选?

  • merge 保留完整的分支历史,是谁在哪个分支上做的、何时合并的,一目了然。适合把功能分支并回主线
  • rebase 让提交历史干净成一条线,但会改写提交历史(hash 变了)。适合在 push 之前整理自己的功能分支

黄金法则:永远不要对已经 push 出去、别人可能基于它工作的分支做 rebase。 否则你会把同事的本地仓库搞乱。

rebase 的交互模式是整理提交历史的神器:

bash 复制代码
git rebase -i HEAD~4   # 整理最近 4 次提交

它会打开编辑器,让你对每一次提交做:合并(squash)、改写信息(reword)、调整顺序、丢弃等。

只想搬某一次提交过来?用 cherry-pick

bash 复制代码
# 把指定的提交"摘"过来,复制到当前分支顶部
# 常见场景:在 main 修了个 hotfix,再把它同步到 develop / 旧版本分支
git cherry-pick <commit-id>

merge / rebase 是把整条分支 并过来,cherry-pick 是只搬单个提交,三者按需选用。

3. 解决冲突

当两个分支改了同一处代码,Git 无法自动合并时,就会产生冲突。文件里会出现:

markdown 复制代码
<<<<<<< HEAD
这是当前分支的内容
=======
这是被合并进来的内容
>>>>>>> feature/login

处理流程:

bash 复制代码
git merge feature/login      # 提示冲突
git status                   # 查看哪些文件冲突

# 手动编辑文件,删掉 <<<<<<< ======= >>>>>>> 标记,保留正确内容
# 改完后标记已解决
git add 冲突文件
git commit                   # 完成合并提交

# 如果改到一半想放弃合并
git merge --abort

如果冲突发生在 rebase 过程中 ,处理方式几乎一样(编辑文件 → git add),只是收尾命令不同------这是最容易记混的一点:

bash 复制代码
# 解决完当前冲突,继续变基(注意:这里不要用 git commit)
git add 冲突文件
git rebase --continue

# 想放弃整个变基,回到 rebase 前的状态
git rebase --abort
# 想跳过当前这个冲突的提交
git rebase --skip

冲突不可怕,怕的是乱删。原则是:读懂两边的意图,手动拼出正确结果,而不是简单删掉一边。

4. 撤销与回退:Git 的"后悔药"

这是最容易出错、也最该学透的一块。先记住一个心智模型------一次改动会依次穿过三个区域,走到哪一步、就用哪条命令把它撤回来:

perl 复制代码
    add          commit         push
工作区 ──→ 暂存区 ──→ 提交历史 ──→ 远程仓库
  │           │             │
 restore   restore --staged  reset / revert
                          (已 push 必须用 revert)

先用这张表对号入座(细节看下面的场景):

改动走到了哪一步 撤销命令 是否影响别人
还没 add(在工作区) git restore <file>
已经 add(进了暂存区) git restore --staged <file>
已经 commit(本地) git reset --hard <commit> / git revert 否(还没 push)
已经 push(到了远程) git revert <commit>(反向提交) 是,必须用 revert

场景 A:改坏了工作区的文件,还没 add,想丢弃改动

bash 复制代码
# 丢弃单个文件的工作区改动(Git 2.23+ 推荐)
git restore 文件名
# 老写法
git checkout -- 文件名
# 丢弃所有工作区改动
git restore .
# 只丢弃部分代码块(和 git add -p 对称,精细撤销)
git restore -p

场景 B:已经 add 了,想撤出暂存区(不丢内容)

bash 复制代码
git restore --staged 文件名
# 老写法
git reset HEAD 文件名

场景 C:已经 commit 了,想修改最近一次提交

bash 复制代码
# 追加改动到上一次提交,或只改提交信息
git commit --amend

⚠️ amend改写提交历史 。如果这次提交已经推到共享分支(如 main/develop),尽量不要 amend ,否则同事拉取会撞冲突。只有自己的功能分支、且确认没人基于它工作时才能强推,而且务必用 --force-with-lease 而非 --force------它会在远程有别人新提交时拒绝推送,不会覆盖同事的代码:

bash 复制代码
git push --force-with-lease

场景 D:想回退到某个历史版本

这里 resetrevert 是两个完全不同的东西,务必分清:

bash 复制代码
# reset:把当前分支指针"倒退"到指定提交,之后的提交会消失
git reset --hard <commit-id>   # 工作区也一起清空(危险!改动全没了)
git reset --soft  <commit-id>  # 只移动指针,改动全保留在暂存区
git reset --mixed <commit-id>  # 移动指针,改动退回工作区(默认)

# revert:生成一个"反向提交"来抵消某次改动,不改写历史
git revert <commit-id>

怎么选?

  • 改动还没 push ,想抹掉重来 → reset
  • 改动已经 push、别人在用这个分支 → 用 revert,安全,不破坏历史。

git reset --hard 是 Git 里最危险的命令之一,敲之前想清楚:丢掉的东西能不能用 git reflog 找回来(见文末技巧)。

5. 临时藏起改动:stash

你正在 feature 分支写代码写了一半,突然要切到 main 修个紧急 bug,又不想提交半成品。stash 就是干这个的。

bash 复制代码
# 把已跟踪文件的未提交改动临时存起来,工作区变干净
# 注意:默认不含 untracked(新建、还没被 git 管理过)的文件
git stash
# 想连未跟踪的新文件一起存,加 -u
git stash -u
# 存的时候起个名字
git stash push -m "登录页改了一半"

# 查看存了哪些
git stash list

# 取出来继续干(默认取最近一次)
git stash pop
# 取出来但不从 stash 列表删除
git stash apply
# 删除某次 stash
git stash drop stash@{0}

典型工作流

bash 复制代码
git stash                # 藏起 feature 上的半成品
git switch main          # 切走修 bug
# ... 修复、提交、推送 ...
git switch feature
git stash pop            # 把半成品取回来,接着写

6. 打标签:tag

标签用来标记发布版本 ,比如 v1.0.0。它是一个固定的、不会移动的指针,比 commit id 好记。

bash 复制代码
# 给当前提交打轻量标签
git tag v1.0.0

# 打带说明的标签(推荐,相当于一个正式的发布记录)
git tag -a v1.0.0 -m "首个正式版本"

# 给某个历史提交补打标签
git tag -a v0.9.0 <commit-id>

# 查看所有标签
git tag

# 推送标签到远程(push 默认不带标签!)
git push origin v1.0.0
git push origin --tags    # 一次推全部

7. 管理远程仓库:remote

bash 复制代码
# 查看已配置的远程仓库
git remote -v

# 添加一个远程(比如 fork 后关联上游仓库)
git remote add origin https://github.com/you/repo.git
git remote add upstream https://github.com/original/repo.git

# 修改远程地址
git remote set-url origin git@github.com:you/repo.git

# 删除远程
git remote remove upstream

Fork 工作流 常用:定期从 upstream(原仓库)同步更新:

bash 复制代码
git fetch upstream
git switch main
git merge upstream/main
git push origin main

8. 追查"这行代码是谁写的":blame

线上出 bug,想知道某行代码是谁、在哪个提交里加的,blame 逐行显示每行代码的最后修改者和提交:

bash 复制代码
git blame path/to/file.ts
# 只看第 10-20 行
git blame -L 10,20 path/to/file.ts

不是为了甩锅,而是为了找到对应的提交,git show <commit-id> 看完整改动上下文,理解这段代码为什么这么写。

9. 引用独立仓库:submodule

当项目需要复用另一个独立的 Git 仓库(比如公共组件库、内部 SDK、共享的配置仓库),又不想把它的代码直接拷进来(那样就没法独立更新、也无法保留它自己的历史),就用 submodule(子模块)

它的本质是:在你的仓库里,嵌套记录另一个仓库的「某个特定提交」。主仓库保存的不是子模块的全部内容,而是一个指向子模块某次提交的指针。

① 添加一个子模块:

bash 复制代码
git submodule add https://github.com/team/ui-components.git libs/ui

这会做三件事:把 ui-components 克隆到 libs/ui、生成一个 .gitmodules 文件(记录 URL 和路径)、并把这两项改动放进暂存区。你需要把它们提交掉:

bash 复制代码
git commit -m "添加 ui-components 子模块"

② 克隆一个含子模块的项目:

普通的 git clone 不会自动拉取子模块,子模块目录会是空的。需要额外一步:

bash 复制代码
# 方式一:克隆时一并初始化
git clone --recurse-submodules https://github.com/you/repo.git

# 方式二:已经克隆过了,补拉子模块
git submodule update --init --recursive

--recursive 是因为子模块本身可能还嵌套着它自己的子模块。团队协作时这一步最容易被忘记,新人拉下来发现目录是空的就是这个原因。

③ 更新子模块到它的最新版本:

子模块默认「钉」在添加时指定的那个提交上。子模块仓库发布了新版本,你想跟上来:

bash 复制代码
# 偷懒写法:把所有子模块都拉到各自远程的最新提交
git submodule update --remote

# 或手动控制:进到子模块里拉取,再回主仓库记录新指针
cd libs/ui
git switch main && git pull
cd ../..
git add libs/ui
git commit -m "升级 ui-components 子模块到最新"

关键认知:主仓库并不关心子模块的分支,只关心它指向哪个 commit。 所以上面的操作本质是在主仓库提交了一次「子模块指针的移动」。

④ 修改子模块里的代码:

libs/ui 里改、提交、推送,和普通仓库完全一样。唯一要注意的是:先切到一个分支,否则你会处于 detached HEAD(游离头指针)状态,提交容易丢失。

bash 复制代码
cd libs/ui
git switch -c fix/button-color   # 先切分支,避免 detached HEAD
# 改代码 → commit → push 到子模块自己的远程
git push
cd ../..
git add libs/ui
git commit -m "主仓库:子模块修复按钮颜色"

⑤ 删除子模块(比添加麻烦,记住这几步):

bash 复制代码
git submodule deinit -f libs/ui      # 1. 取消初始化
git rm -f libs/ui                    # 2. 从工作区和暂存区移除
rm -rf .git/modules/libs/ui          # 3. 删除 git 内部的元数据
git commit -m "移除 ui-components 子模块"

submodule 的几个常见坑:

  • 克隆忘记加 --recurse-submodules → 目录是空的,用 git submodule update --init --recursive 补救。
  • 切换主仓库分支后子模块「没跟上」,状态显示 dirty → git submodule update 把指针移到位。
  • 团队协作时,主仓库里「子模块的新指针」和 .gitmodules 必须一起提交推送,否则同事拉下来会对不上。
  • 如果只是想用一个库、并不打算跟踪它的更新,考虑用包管理器(npm / Gradle / CocoaPods)替代 submodule,往往更省心。

第三层:典型团队工作流

命令是零件,工作流是蓝图。下面是最常用的两种。

GitHub Flow(轻量,适合持续部署)

bash 复制代码
main(永远可发布)
 ├── feature/login     →  写完发 PR →  review →  合并回 main
 └── feature/payment   →  ...
  1. git switch -c feature/xxx 开分支
  2. 写代码,频繁 commit
  3. git push -u origin feature/xxx
  4. 在平台上发起 Pull Request
  5. review 通过后合并(通常用 squash merge 让历史干净)
  6. 删掉本地分支 git branch -d feature/xxxgit pull 同步

Git Flow(重量,适合有明确版本发布的项目)

  • main:生产环境
  • develop:日常开发集成
  • feature/*:功能分支,从 develop 切出,合回 develop
  • release/*:发布准备分支
  • hotfix/*:紧急修复,从 main 切出,同时合回 maindevelop

中小团队通常用 GitHub Flow 就够了,别过度设计。


速查表

场景 命令
配置用户名 git config --global user.name "名字"
配置邮箱 git config --global user.email "邮箱"
克隆仓库 git clone <url>
查看状态 git status / git status -s
暂存改动 git add . / git add -p
提交 git commit -m "msg"
看历史 git log --oneline --graph --all
看改动 git diff / git diff --staged
推送/拉取 git push / git pull
新建并切换分支 git switch -c <branch>
合并分支 git merge <branch>
变基 git rebase main / git rebase -i HEAD~3
摘取单个提交到当前分支 git cherry-pick <commit>
丢弃工作区改动 git restore <file>
精细丢弃部分工作区改动 git restore -p
撤出暂存区 git restore --staged <file>
修改最近一次提交 git commit --amend
安全回退已 push 的提交 git revert <commit>
抹掉未 push 的提交 git reset --hard <commit>
临时存改动 git stash / git stash pop
打版本标签 git tag -a v1.0.0 -m "..."
看每行代码归属 git blame <file>
添加子模块 git submodule add <url> <path>
克隆含子模块的项目 git clone --recurse-submodules <url>
初始化 / 补拉子模块 git submodule update --init --recursive
更新子模块到最新 git submodule update --remote

几条避坑经验

1. 永远不要在 main 上直接干活。 开分支成本几乎为零,但在 main 上写废了回退成本极高。

2. 提交要小而频繁,信息要能看懂。 半天攒一大坨、信息写 update 的提交,三个月后没人看得懂(包括你自己)。

3. push 之前先 pull / fetch。 能把冲突消灭在本地,而不是推到远程后炸掉 CI。

4. 不要对共享分支做 rebasepush --force--force-with-lease 替代 --force,它会在远程有别人新提交时拒绝推送,避免覆盖同事的代码。

5. reset --hard 之前先 git stashgit branch backup 给自己留个后悔的余地。

6. 误删了提交?git reflog 是救命稻草。 它记录了 HEAD 的每一次移动,哪怕 reset --hard 抹掉的提交,只要没被垃圾回收,都能用 git reflog 找到对应 id,再 git reset --hard <id> 回去:

bash 复制代码
git reflog
git reset --hard <reflog 里的 commit-id>

结语

Git 命令虽多,但日常开发真正高频的就十来条,团队协作里关键的也集中在分支、合并、回退、冲突这几块。把第一层用成肌肉记忆,把第二层理解透背后的"工作区/暂存区/提交历史"模型,你就已经超过大多数开发者了。

剩下的高级命令(cherry-pickbisectworktree 等),等遇到对应场景再查也来得及。工具是为流程服务的------先建立好工作习惯(小步提交、规范分支、写清楚信息),Git 自然会成为你的帮手,而不是负担。

相关推荐
叫我少年1 天前
Windows 中安装 git
git
深海鱼在掘金6 天前
Git 完全指南 —— 第1章:Git 概览与版本控制演进
git
noravinsc7 天前
关于Git Flow
git
蜜獾云7 天前
在Git中配置用户名和密码
git
scx_link7 天前
通过git bash在本地创建分支,并推送到远程仓库中
开发语言·git·bash
南大白7 天前
IntelliJ IDEA 运行时的 JVM 本地内存溢出崩溃
git
码农小旋风7 天前
Claude Code 基础用法大全:对话、分析、修改、测试、Git 和工作流
人工智能·git·chatgpt·claude
南大白7 天前
Git 撤回提交完整方案
git
像风一样的男人@7 天前
python --实现代理服务器
git·ui