☰
Git常用命令详解:从初始化到分支合并的实战流程
2026/9/28 5:18:16 网站建设 项目流程

用了这么多年Git,我发现一个现象:办公室里干活最利索的同事,往往只用那二十来个命令就把需求安排得明明白白;而每次遇到合并冲突就抓瞎的人,手头却存着上百条冷门命令的速查表。Git常用命令这个事,从来不是比谁背得多,而是比谁把核心工作流跑得顺。

这篇文章我想把日常开发里真正高频使用的Git命令按真实场景串一遍——不是从一个命令跳到另一个命令的命令字典,而是沿着“初始化→提交→分支→远程→改写→救火”这条实际工作路径,讲讲每个命令出现的时机和背后的机制,顺手把我踩过的坑也一并交代了。适合刚接触Git没几天的新手,也适合长期用GUI工具、想转命令行的同学。读完你不需要记住所有命令,只需要理解这条链路,其他的随时查就行。

1. 为什么我还在用命令行使唤Git

有人可能会问:现在SourceTree、VS Code、IDEA自带的Git面板都做得挺好了,为什么还要折腾命令行?我的回答很直接:命令行是唯一能让你真正看懂Git在干什么的方式。

1.1 GUI工具说不清楚的那些事

GUI的问题不在于不好用,而在于它把每一步都封装成了按钮。点击“Commit”按钮,代码提交了;点击“Push”按钮,代码上去了。听起来没问题,可一旦出现冲突、提交错文件、想撤销某次操作,GUI只会弹出一个确认框,你根本不知道背后发生了什么,也不知道还有什么补救手段。

命令行则强迫你看清楚每一次操作的对象和位置。git status告诉你工作区什么状态,git log告诉你有几次提交,git diff告诉你改了什么。每敲一条命令,你都在和仓库的实际状态对话,而不是隔着GUI点来点去。我见过太多人用GUI推错分支、把代码commit到错误仓库,就是因为界面上的分支切换太隐蔽了。

1.2 命令行的真正优势:可复现、可脚本化

命令行第二个优势是能写成脚本。举个真实场景:我有个项目发版前要把版本号从1.2.3改成1.3.0,然后打tag、推远程、触发CI。这一串操作如果全用GUI点,每一步都可能手滑;如果用命令写成一个shell脚本,每次发版执行一次即可。

还有一个常被忽略的好处:命令行输出的信息非常透明。git push被拒绝时它会直接告诉你“non-fast-forward”,然后提示你可以用git pull先合并。这种明确的反馈在图形界面里经常被简化成一句“提交失败”,至于为什么失败、怎么解决,全要靠猜。

所以我的建议是:GUI可以用来查看历史、快速对比文件,但所有会改变仓库状态的操作,尽量养成用命令行的习惯。这也是为什么下面整篇文章都围绕命令展开,而不是某个IDE的操作演示。

2. 环境准备:从安装到第一次提交

工欲善其事,必先利其器。想跑通Git命令,先把环境和基础配置弄明白,这一步卡住的人其实不少。

2.1 不同系统的安装方式一览

操作系统安装方式备注
Windows下载Git官方安装包,一路Next即可安装时建议选择“Git from the command line”,这样Git bash和CMD都能直接调用git
macOSbrew install git直接用Homebrew装最方便
Ubuntu/Debiansudo apt install git优先用系统源的版本,稳定优先
CentOS/RHELsudo yum install git老系统注意源里的版本可能偏旧

Windows上装Git时,我强烈建议顺带把Git Bash一起装上。它不只给一个Linux风格的终端,还自带ssh、vim等工具,省去很多额外配置。很多人第一次接触Linux常用命令就是靠Git Bash学起来的——ls、cd、mkdir、grep这些在Git Bash里都能直接跑,对Windows用户来说是一扇通向命令行世界的大门。

2.2 全局配置:git config那些必填项

装完Git第一件事不是clone仓库,而是配置身份信息。没有这一步,你连commit都提交不了:

git config --global user.name "你的名字" git config --global user.email "你的邮箱"

这里有个细节需要注意:邮箱最好和你常用的代码托管平台账号保持一致,否则提交记录里的头像和用户名串不起来。我见过有人公司邮箱和个人邮箱混着提交,到年底看贡献统计时数据看着很乱。

除了身份信息,还有两个配置我建议顺手加上:

git config --global core.autocrlf input git config --global core.quotepath false

core.autocrlf解决的是Windows和Linux之间换行符差异问题。设成input的意思是:提交时把CRLF转成LF,拉取时不强制转换,这样能避免因为换行符导致整个文件显示被改动。core.quotepath false则是让中文文件名正常显示,而不是显示成\346\265\213\350\257\225那串转义数字,对国内开发者来说几乎是必选项。

2.3 第一次init与commit

进入一个新项目目录,敲下面三条命令完成初始化:

git init git add . git commit -m "Initial commit"

git init会在当前目录里生成一个隐藏的.git文件夹,这个文件夹里装着的就是整个仓库的全部历史。你不小心删掉它,Git会瞬间失忆,所以千万别去动它。

如果你只是在已有仓库里干活,则不需要git init,而是用git clone把远程仓库拉下来。克隆和初始化的区别这里先提一嘴:前者是复制一个已经存在的仓库,后者是在空白目录里新建一个仓库。

2.4 我第一次用Git时的翻车现场

我头一回用Git是在Windows上,直接双击Git Bash,然后敲git commit,结果报了一串英文错误说我连姓名都没设置。当时完全不知道要配git config,跑去搜索引擎搜了半天才弄明白。后来带新人我就学乖了,先让他们把git config --list跑一遍,确认配置没问题再开始干活。

3. 日常工作流:status、add、commit、log、diff组合

这一套命令占据了日常Git操作至少八成,也是理解Git一切机制的地基。

3.1 status:每天开工的第一条命令

git status

这条命令没有任何副作用,只是把当前仓库的状态列出来:哪些文件被修改了、哪些文件是新加的、当前在哪个分支上、和远程分支比是领先还是落后。我自己的习惯是每次准备提交前一定会跑一次git status,确认自己到底要提交哪些东西。

刚用Git的人最容易犯的错是git add .一把梭,把所有文件都塞进暂存区。等提交完才发现里面混进来一个.env环境变量文件,密钥已经推到仓库里了。后来我给自己定了个规矩:git add之前必须先看status,想清楚再动手。

3.2 add:理解暂存区这个概念

git add 文件名 # 添加指定文件 git add . # 添加当前目录所有改动 git add -u # 只添加已跟踪文件的修改和删除 git add -p # 交互式选择要暂存的代码块

为什么要设计一个“暂存区”?因为Git想让你把提交拆成逻辑清晰的小块。你可以上午改了两个功能,下午改了一个Bug,通过add选择性地把Bug相关的文件先提交,再提交两个功能。这相当于让你在写提交信息之前,先整理一遍到底要记录哪些变更。

[ 工作区(working directory)\xrightarrow{git add}暂存区(staging area)\xrightarrow{git commit}本地仓库(local repository) ]

3.3 commit:写好message也是命令的一部分

git commit -m "feat: 添加用户注册功能"

提交信息不是写给自己看的,是写给未来的自己和团队看的。三个月后再回来看这条提交,如果信息只写着“fix”,你根本不知道修了什么。我习惯用一套简单的约定:

  • feat:新功能
  • fix:修Bug
  • docs:文档变更
  • refactor:重构(不改变行为)
  • test:测试相关

git commit --amend命令在这条分支上还有一个特殊用途:如果你刚才提交的message写错了、或者漏提交了一个文件,可以在下一次提交前“修补”这次提交:

git add 漏掉的文件 git commit --amend

这句话的含义是:把当前暂存区的改动合并到上一次提交里,而不是生成一个新提交。执行后会打开编辑器让你修改message,也可以直接加参数:

git commit --amend -m "修正后的提交信息"

需要注意,amend会改变提交的哈希值。如果这个提交已经推送到了远程、而且别人已经基于它做了新提交,那就不要amend了,否则会把团队历史搅成一锅粥。只有在提交还没推送、或者你确定只有自己在用这条分支时,amend才是安全的。

3.4 log和diff:复盘与审查

git log --oneline --graph --decorate -20

这是我最常用的log命令:--oneline压缩成一行显示,--graph用字符画出分支图,--decorate标记出tag和分支名,-20只看最近20条提交。打开以后整个仓库的历史脉络一目了然。

git diff负责看具体改动内容:

git diff # 工作区对比暂存区 git diff --staged # 暂存区对比本地仓库(也就是即将提交的内容) git diff <commit1> <commit2> # 对比任意两次提交

git diff --staged在提交前的审查价值极高。Commit前跑一条,你会看到Git到底准备记录哪些改动,能拦截掉八成“提交了不该提交文件”的事故。

4. 分支操作:创建、合并与冲突处理

分支是Git用来并行开发的核心能力,但很多人用着用着就撞锁了,主要原因是没有理解分支的本质。

4.1 分支的本质:一个会移动的指针

在Git里,分支并不是一个文件夹,而是一个指向某个提交的指针。你执行git branch dev,其实就是创建了一个叫dev的指针,指向当前HEAD所在的提交。

[ \text{分支指针} \rightarrow \text{commit对象} \rightarrow \text{父提交} \rightarrow \cdots \rightarrow \text{初始提交} ]

理解了指针模型,很多问题就迎刃而解:

  • 新分支和原分支“共享历史”是很正常的事,因为指针本来就指向同一个提交。
  • 切换分支只是改变HEAD指向的指针,工作区文件会跟着切换,所以Git才会要求你在切换前把工作区清理干净。
  • 合并分支本质上是把某条分支上的提交“接”到另一条分支上。

4.2 高频分支命令速查

git branch # 查看本地分支 git branch -a # 查看所有分支,包括远程分支 git checkout -b feature/login # 创建并切换分支 git switch -c feature/login # 新版Git的推荐写法 git merge feature/login # 把feature/login合并到当前分支 git branch -d feature/login # 删除已合并的分支

这里我要特别提一下git switch。Git 2.23之后官方把checkout拆成了switch和restore两个命令,switch专门负责分支切换,restore负责文件恢复。对新手来说,这个拆分能大大降低困惑度:想换分支就用switch,想还原文件就用restore,不用再纠结checkout那一个命令怎么变出多种行为。

4.3 合并冲突的完整排查链路

合并冲突是Git新手最害怕的事,但其实只要走一遍完整排查链路,解决起来非常机械。假设我在main分支要合并feature/login分支:

git checkout main git merge feature/login

如果两边都改了同一个文件的同一行,Git会输出CONFLICT并告诉你哪个文件冲突。这时候很多人第一反应是慌,其实只需要三步:

第一步,查看冲突文件和冲突区域:

git status

冲突文件会被标记为both modified。打开这个文件,你会看到这样的标记:

<<<<<<< HEAD 你当前分支的代码 ======= 正在合并进来的分支的代码 >>>>>>> feature/login

**第二步,手动解决每一处冲突。**保留你想要的内容,把<<<<<<<、=======、>>>>>>>这些标记行删掉。如果两边都想要,就把两者拼起来;如果要结合,就得自己写逻辑。这一步没有自动化手段,必须人来判断。

第三步,标记为已解决并提交:

git add 冲突文件 git commit -m "merge: 合并feature/login"

有些人解决完冲突后只add就以为完事了,结果分支一直停在MERGING状态,下次操作出现各种莫名其妙的现象。记住,合并冲突解决完必须有一次commit,这次commit就是“合并提交”,它把两条分支的历史正式缝合在一起。

4.4 一个小技巧:合并前先看分叉情况

执行merge之前,我先跑一条:

git log --oneline --graph --left-right --cherry-pick HEAD...feature/login

这能直接看出两个分支之间有多少提交、哪些已经在对方分支上存在。如果两边其实没什么分叉,Git走的是「快进合并」(fast-forward),连冲突都不会有。先看清楚再合并,比闷头git merge然后被冲突淹没舒服得多。

5. 远程协作:clone、push、pull与SSH配置

5.1 remote概念与clone

git clone git@gitee.com:你的用户名/项目名.git

git clone做了三件事:把远程仓库完整下载到本地、把远程地址记成origin、自动建一个本地分支跟踪远程分支。执行完之后你什么都不用配置就能直接开发。

如果你的项目在Gitee上,我建议优先用SSH而不是HTTPS。HTTPS每次push都要输账号密码,SSH配置一次就能一直免密。配置SSH密钥的流程如下:

ssh-keygen -t ed25519 -C "你的邮箱"

一路回车会在~/.ssh/下生成id_ed25519(私钥)和id_ed25519.pub(公钥)。公钥可以随便给别人看,私钥绝对不要泄露。然后执行:

cat ~/.ssh/id_ed25519.pub

把输出的公钥内容复制到Gitee个人设置里的“SSH公钥”页面保存。之后测试连通性:

ssh -T git@gitee.com

如果看到欢迎信息就说明连上了。这里有个易踩的坑:Windows用户用Git Bash执行ssh-keygen时可能遇到权限问题,检查一下~/.ssh目录的所有者和权限,私钥文件权限如果是-rw-r--r--,SSH会认为不安全并拒绝使用。

5.2 push与pull:同步的本质

git push origin main git pull origin main

push是把本地提交推送到远程,pull是把远程提交拉下来并合并。日常开发里标准姿势是:开发前先git pull同步最新代码,开发完git commit,再git push。

很多人会遇到一个令人费解的报错:

git push被拒绝:! [rejected] 因为远程有本地没有的提交

这不是什么大问题,本质是远程分支比本地先进,Git不允许你直接覆盖。解决方式也简单:

git pull origin main # 解决可能的冲突,或者让Git自动完成合并 git push origin main

另一种思路是git pull --rebase,它会把你本地的提交先“摘下来”,拉到远程最新提交之后再把你自己的提交“放”到最上面,形成一条线性历史。关于rebase和merge的争论很多,我的建议是:如果你能理解rebase是“重放提交”,那就用;如果还不太理解,用普通的pull更稳妥。等对Git有感觉了再切换也不迟。

5.3 fetch与pull的区别

git fetch origin

fetch和pull最大的区别是:fetch只是把远程的提交下载到本地,但不合并到当前分支;pull则是fetch加merge两步一起做。

有时候我不想立刻合并,只想看看远程改了什么,就先fetch,再用git log HEAD..origin/main对比差别。确认没有可疑提交之后才merge。对于多人协作的大型仓库,这个习惯能避免很多莫名冲突。

5.4 远程分支的追踪关系

git clone之后,本地main会自动跟踪origin/main,所以直接git pull/git push即可。但是你自己创建的新分支,第一次push时要用:

git push -u origin feature/login

-u的作用是建立本地分支和远程分支的追踪关系。之后在这个分支上再push/pull就不用带远端参数了。如果漏了-u,每次都要写全git push origin feature/login,也不是不行,就是很烦。

6. 历史改写:amend、reset、rebase与reflog

Git最强大的地方是历史可以修改,最危险的地方也是历史可以修改。这一节讲讲常用的“后悔药”和它们的适用范围。

6.1 reset:回退提交的三种模式

git reset --soft HEAD~1 git reset --mixed HEAD~1 git reset --hard HEAD~1

三种模式的差异用一张表说清楚:

模式HEAD指向暂存区工作区适用场景
--soft回退保留保留想重新整理提交信息
--mixed回退清空保留想重新add后分多次提交
--hard回退清空清空想彻底丢弃改动

git reset --hard会直接删除工作区所有未提交的改动,而且很难找回。我在带新人时反复强调:使用--hard之前必须先确认工作区的改动是否已经备份或推送到远程,否则一把下去,一上午白干。

6.2 reflog:Git的“后悔药”保险柜

git reset --hard之后你以为改动没了,其实Git还有一个隐藏机制叫reflog,它会记录所有分支指针的移动历史,包括被reset掉的提交:

git reflog

输出里能看到每一行:HEAD@{0}: reset: moving to HEAD~1,后面跟着一堆哈希值。假如你不小心reset --hard删了三次提交,只要在上面的记录里找到那个“删之前”的哈希,然后:

git reset --hard 恢复目标哈希

就能把分支指针拨回误删之前的状态。reflog只在本地有效,而且有90天的过期时间,但它是本地恢复操作里最可靠的一道保险。

6.3 rebase:整理一串提交

git rebase -i HEAD~3

这条命令会打开一个交互式编辑界面,让你对最近3次提交做处理。常见操作有:pick保留、squash把多个提交合并成一个、reword修改提交信息、drop删除提交。

我建议一个功能一个提交,不要在代码里出现“fix2”“fix3”“fix4”这种噪音提交。开发周期结束前用git rebase -i把这几条合并成一条干净的提交,配合上一条补丁,历史看起来会很舒服。

6.4 绝对不要改写的场景

所有修改历史提交哈希的命令(amend、rebase、reset --hard之后的push --force),都不要对已经推送到远程、并且别人可能已经拉取过的分支使用。改写共享历史等于告诉别人“你拉到的那个提交根本不存在”,会让整个团队进入混乱状态。

如果确实需要修正已推送的提交,一种相对安全的方式是提交一个“反向提交”,把改动回滚掉,然后正常push,而不是强制改写历史。

7. 高频报错排查:从fatal到一切正常

命令行用多了,总会遇到报错。这一节是我遇到过、也带新人遇到次数最多的几个问题,完整走一遍排查链路。

7.1 fatal: not a git repository (or any of the parent directories): .git

这个报错典型症状是:在某个目录下敲git log、git status,结果提示这根本不是Git仓库。

根因分析:当前目录不是仓库,或者当前目录不在任何仓库的子目录里。Git找仓库的方式是逐级向上找.git文件夹,一直找到文件系统根目录都找不到就报这个错。

排查链路:

  1. 执行pwd确认当前路径在哪。
  2. 执行ls -a看当前目录有没有.git文件夹。
  3. 如果不在仓库内,切回仓库目录:cd /path/to/repo。
  4. 如果不知道仓库在哪,用find / -name ".git" -type d 2>/dev/null全盘搜一下(Linux/macOS/Git Bash下可用)。

有一次我帮同事看问题,他坚称自己就在仓库里,结果一查,原来他在仓库的上一层目录建了个子文件夹,把代码复制进去开发了。git只认.git的位置,不认文件夹名字,复制到新目录后原来那个.git并不会跟着过来。

7.2 其他高频报错速查表

报错信息大概率原因解决办法
Please tell me who you are未配置user.name/user.email执行git config --global user.name和user.email
src refspec main does not match any本地还没有任何提交就push先git commit再push
failed to push some refs远程有本地没有的提交git pull后再push
Your branch is ahead of 'origin/main' by 1 commit本地领先远程git push
fatal: refusing to merge unrelated histories两个仓库没有共同历史就合并要合并两个独立仓库时加--allow-unrelated-histories;如果是误操作,检查自己是不是clone错了仓库
Permission denied (publickey)SSH密钥没配好检查公钥是否添加到托管平台,私钥是否存在且权限正确
The file will have its original line endings换行符转换提示配置core.autocrlf,一般不影响功能

这里面refusing to merge unrelated histories值得一提。有次我初始化本地仓库后,又执行git remote add origin想关联远程,然后git pull直接报这个错。原因是我本地的初始提交和远程仓库完全没有共同祖先。解决办法很简单:要么直接git clone而不是先init再remote,要么确实需要合并两个独立项目时用--allow-unrelated-histories。搞清楚是哪种情况,比硬加参数重要。

7.3 一条被我盯了很久的冷门命令

热搜词里有一条看起来很奇怪的命令:

git -c diff.mnemonicprefix=false -c core.quotepath=false --no-optional-locks

这其实不是一条“常用命令”,而是某些IDE或作工具在调用Git时自动加上的配置参数。-c表示临时设置某个配置项而不写入全局配置,--no-optional-locks是告诉Git不要在某些纯读操作里创建不必要的锁文件。如果你在进程列表或日志里看到类似的长串命令,不用慌,这是工具在用Git,不代表你敲错了命令。

写在最后:我的一点个人体会

Git命令行这套东西,上手时觉得别扭,用久了反而离不开。现在我一般进一个项目目录,先git status看状态,再git log --oneline -10看最近的提交,心里对整个项目的进展脉络就很清晰了。这种“掌握感”是GUI给不了的。

再分享一个小技巧:如果你总记不住命令,别硬背,在~/.bashrc或~/.bash_profile里设置一些短别名。我自己常配的是:

alias gs='git status' alias ga='git add' alias gc='git commit -m' alias gl='git log --oneline --graph --decorate -20' alias gp='git push'

现在开发新项目,只需要ga、gc、gp三个别名就能完成八成日常操作。剩下的命令遇到再查,Git的提示本身已经很友好了,报错了就按它说的改,改几次自然就记住了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询