ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Git常用命令详解:从初始化到分支合并的实战流程

Git常用命令详解:从初始化到分支合并的实战流程 用了这么多年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都能直接调用gitmacOSbrew 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 falsecore.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 commitgit 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:修Bugdocs:文档变更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概念与clonegit clone gitgitee.com:你的用户名/项目名.gitgit 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 gitgitee.com如果看到欢迎信息就说明连上了。这里有个易踩的坑Windows用户用Git Bash执行ssh-keygen时可能遇到权限问题检查一下~/.ssh目录的所有者和权限私钥文件权限如果是-rw-r--r--SSH会认为不安全并拒绝使用。5.2 push与pull同步的本质git push origin main git pull origin mainpush是把本地提交推送到远程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 originfetch和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与reflogGit最强大的地方是历史可以修改最危险的地方也是历史可以修改。这一节讲讲常用的“后悔药”和它们的适用范围。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 reflogGit的“后悔药”保险柜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文件夹一直找到文件系统根目录都找不到就报这个错。排查链路执行pwd确认当前路径在哪。执行ls -a看当前目录有没有.git文件夹。如果不在仓库内切回仓库目录cd /path/to/repo。如果不知道仓库在哪用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.emailsrc refspec main does not match any本地还没有任何提交就push先git commit再pushfailed to push some refs远程有本地没有的提交git pull后再pushYour branch is ahead of origin/main by 1 commit本地领先远程git pushfatal: 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.mnemonicprefixfalse -c core.quotepathfalse --no-optional-locks这其实不是一条“常用命令”而是某些IDE或作工具在调用Git时自动加上的配置参数。-c表示临时设置某个配置项而不写入全局配置--no-optional-locks是告诉Git不要在某些纯读操作里创建不必要的锁文件。如果你在进程列表或日志里看到类似的长串命令不用慌这是工具在用Git不代表你敲错了命令。写在最后我的一点个人体会Git命令行这套东西上手时觉得别扭用久了反而离不开。现在我一般进一个项目目录先git status看状态再git log --oneline -10看最近的提交心里对整个项目的进展脉络就很清晰了。这种“掌握感”是GUI给不了的。再分享一个小技巧如果你总记不住命令别硬背在~/.bashrc或~/.bash_profile里设置一些短别名。我自己常配的是alias gsgit status alias gagit add alias gcgit commit -m alias glgit log --oneline --graph --decorate -20 alias gpgit push现在开发新项目只需要ga、gc、gp三个别名就能完成八成日常操作。剩下的命令遇到再查Git的提示本身已经很友好了报错了就按它说的改改几次自然就记住了。
返回列表