1. 先说说Git到底是什么,为什么它几乎成了开发标配
Git这东西,刚接触的时候总觉得有点绕,等真用熟了,你会发现它其实就解决了两件事:第一,帮你把代码的历史版本管得明明白白;第二,让一群人能在同一个项目上各改各的,最后还能不打架。你要是翻招聘JD,几乎每个技术岗位都会写“熟悉Git”,但它跟那些需要背命令的软件不一样,Git的核心是它的“思维模型”——只要心里有那套模型,命令根本不用背,遇到场景自然就知道该用什么。
在我带过的团队里,新同事刚入职时最慌的往往不是业务代码,而是Git操作。分支切错了、代码提交乱了、一不小心把别人的改动覆盖了,这些问题几乎人人都遇到过。而老手和新手的差别,不在于记住了多少条命令,而在于遇到状况时能不能快速判断:“我现在处于哪个状态,我要去哪个状态,哪条命令是安全的”。
这篇就来把Git的完整使用链路过一遍,从安装配置、日常提交、分支管理到远程协作,再加上我这些年踩坑踩出来的实战经验。不管你是刚入行的新人,还是已经写了几年代码但一直靠几招“死命令”撑场面的人,按照这条线走一遍,应该能把Git前后打通。
提示:以下内容以命令行操作为主。虽然现在有各种图形化工具,但命令行的逻辑最完整、表达最清晰。工具会迭代,命令背后的原理几十年不变。而且掌握了命令行,图形工具对你来说就是锦上添花。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备:装好Git只是开始,关键在配置
2.1 三种系统下的安装方式
安装Git的方式看系统来。Windows用户我建议直接去官网下载安装包,一路Next装完就行,安装过程中有个选项要注意:选择“Use Git from the Windows Command Prompt”,这样你就能在CMD和PowerShell里直接敲git命令了。macOS用户最简单的方式是安装Xcode Command Line Tools,终端里敲git --version会弹出安装提示,或者用Homebrew执行brew install git。Linux用户根据发行版走包管理器,Ubuntu/Debian用sudo apt install git,CentOS/RHEL用sudo yum install git,Fedora用sudo dnf install git。
装完之后先验证一下:
bash复制git --version
如果你能看到类似git version 2.43.0的输出,说明安装成功了。这里说个细节:不同系统的Git版本号可能差很多,但不要一味追求新版。Git的协议和核心功能高度稳定,只要不是特别老的版本,日常使用不会有差异。我自己在服务器上还跑着2.30左右的版本,一样很稳。
2.2 安装之后的第一件事:配置身份信息
装完Git第一件事不是急着建仓库,而是先配置用户名和邮箱。这个操作很多人会忽略,但几乎所有的Git报错里,“无法自动识别用户信息”应该能排进前三。原因很简单:每次commit,Git都要记录“谁提交的”,如果你没配置,它只能拦着你让你先配置。
bash复制git config --global user.name "你的名字"
git config --global user.email "你的邮箱"
这两个配置是全局的,意味着你这台机器上所有仓库都默认使用这个身份。--global参数的意思是“写入当前用户的家目录下”,对应的配置文件路径在~/.gitconfig(Windows在C:\Users\你的用户名\.gitconfig)。你也可以用--local只给某个仓库单独配置身份,这样在公司的电脑上,个人项目和公司项目就能用不同的身份提交。
配置完了可以查看一下是否生效:
bash复制git config --list
顺手把默认分支名改成main,这是一个很值得做的习惯:
bash复制git config --global init.defaultBranch main
很多老教程会让你改master,但在当前的开源生态里,main已经是事实标准。改这个的意义在于,新初始化的仓库默认分支名就是main,不会跟你远程仓库的分支名产生差异,省得后面还要手动改。
注意:如果你是Windows用户,建议再执行一条命令
git config --global core.autocrlf true。Windows和Linux/macOS的换行符不同(CRLF和LF的区别),这个配置会自动将Windows下的CRLF转换成LF再提交,避免因为换行符差异导致整个文件被判定为“全部改动”的尴尬情况。macOS和Linux用户则建议设置core.autocrlf input。
2.3 配置SSH密钥:和远程仓库安全通信的前提
如果你只在自己电脑上本地用Git,不跟远程仓库交互,那SSH密钥可以不配。但只要你想往GitHub、GitLab、Gitee这类平台推送代码,SSH密钥就是绕不开的一环。它是你的“身份凭证”,比每次输密码安全也方便得多。
生成密钥的方式很固定:
bash复制ssh-keygen -t ed25519 -C "你的邮箱"
一路回车可以生成在默认位置(~/.ssh/id_ed25519),也可以设置密码短语(passphrase)作为额外的保护层。生成完把公钥添加到代码托管平台:~/.ssh/id_ed25519.pub文件的内容复制到平台的“SSH Keys”设置里就行。
验证是否配置成功:
bash复制ssh -T git@github.com
看到Hi xxx! You've successfully authenticated就说明通了。
3. 核心工作流:add、commit、status背后的逻辑
3.1 先理解三个区域的概念
Git本地操作的核心是“三个区域+一次快照”的模型:工作区(Working Directory)、暂存区(Staging Area/Index)、版本库(Repository)。工作区就是你电脑上能看到的文件目录,暂存区是Git为你准备的“待提交清单”区域,版本库存放着已经提交的历史记录。
我用一个生活化的类比来解释:你在写一篇文章,工作区是你的草稿纸;每次你写完一部分觉得“够好了”,就把这一段誊写到干净的稿纸上,这个过程就是git add;稿纸上的内容是暂存区;当你攒了几段稿子,觉得可以作为一个章节定稿了,就把这一章归档到文件夹里,这就是git commit。定稿归档之后,文件夹里就有了这个章节的完整历史版本。
这个模型的精妙之处在于区分了“你想让Git管理哪些改动”和“哪些改动已经正式记录”。比如你同时改了三个文件,但只对其中两个满意,第三个还在调整中,那就可以只git add那两个,第三个留在工作区——它依然是未跟踪状态,不会混进这次提交里。
3.2 日常开发的标准操作流
初始化一个项目:
bash复制git init
这个命令会在当前目录下创建一个.git隐藏文件夹,里面保存着仓库的全部元数据。然后看看当前状态:
bash复制git status
git status是使用频率最高的命令,它会告诉你当前工作区哪些文件被修改了、哪些文件还没被跟踪。刚开始用Git的人最大的错觉是“这个命令很简单所以不重要”,实际上80%的Git困惑都可以通过git status解决——它会给你提示下一步该怎么做。
把文件加入暂存区:
bash复制git add README.md
git add . # 添加所有改动
git add src/ # 添加某个目录
提交:
bash复制git commit -m "feat: 添加用户登录功能"
这里推荐大家从一开始就养成写规范提交信息的习惯。我见过太多fix bug、update、111这种提交信息,等回滚历史的时候完全看不出来哪次提交做了什么。一个简单实用的规范是<type>(<scope>): <subject>,type可以是feat(新功能)、fix(修复)、docs(文档)、refactor(重构)、test(测试)等。这样日志一拉出来,整个项目的演进脉络清清楚楚。
查看提交历史:
bash复制git log
git log --oneline # 简洁模式,只显示提交哈希和说明
git log --graph # 图形化显示分支合并情况
3.3 工作区、暂存区、版本库之间的回退机制
理解了三个区域,你就掌握了Git最核心的模型,后面的所有操作都是围绕它们展开的。这里重点说说“撤销”和“回退”——这是新手最容易出错的地方。
场景一:改乱了工作区的文件,想恢复到上一次提交的状态:
bash复制git checkout -- filename
注意:这个命令会把工作区的改动全部丢弃,不可恢复。执行前先确认自己是否真的不要这些改动了。
场景二:文件已经add进暂存区,想把它从暂存区退回来:
bash复制git reset HEAD filename
这个操作很安全,它只是把暂存区里的记录移出来,文件改动依然在工作区,不会丢失任何内容。
场景三:commit已经提交了,想撤销这次提交但保留改动:
bash复制git reset --soft HEAD~1
--soft是软撤销,只把HEAD指针移回上一次提交,改动全部保留在暂存区,可以重新commit。完整的撤销场景还有很多,这里先记住基础概念,后面我会单独用一节来讲清楚。
4. 分支管理:Git最值钱的设计,没有之一
4.1 分支的本质是什么
理解了三个区域之后,你就能理解分支了。如果你一直一个人写项目,三个区域完全够用,但一旦要和其他人协作,或者你想同时开发多个互不干扰的功能,分支就必需了。
你可以把分支想象成平行宇宙:主分支(main)是一个稳定的世界,你在里面正常生活;另开一个分支,就是在另一个平行的世界里“搞事情”,搞坏了不影响主世界,搞好了再合并回来。每个分支都独立记录自己的提交历史,互不干扰。Git的底层实现是“一个分支只是一个指针”,指向某一次提交,创建分支的开销几乎为零,所以Git才鼓励“多用分支、大胆分支”。
这种设计解除了一个巨大的心智负担:你可以安安心心地尝试新想法,不用担心改坏主代码。试错了就删除分支,试对了就合并,主分支永远保持稳定。
4.2 分支的日常操作
bash复制git branch # 查看本地所有分支,当前分支前有*号
git branch feature # 创建名为feature的新分支
git checkout feature # 切换到feature分支
或者用一条命令既创建又切换:
bash复制git checkout -b feature
合并分支(先切回到接收合并的分支,再执行merge):
bash复制git checkout main
git merge feature
删除已合并的分支:
bash复制git branch -d feature
还有一个很多人在用的更现代的命令git switch:git switch -c new-branch创建并切换,语义比checkout清晰,因为checkout在Git里承担了多个职责(切分支、恢复文件),switch则专门管分支切换。两个命令都有效,看个人习惯。
4.3 合并冲突的本质与解决流程
分支合并最让人头疼的就是冲突(conflict)。所谓冲突,就是两个分支改了同一个文件的同一个位置,Git不知道该听谁的,只能把决定权交给你。遇到冲突不要慌,这是Git的正常“求救信号”。
冲突的标志性特征是git merge之后出现CONFLICT提示,打开冲突文件,你会看到:
code复制<<<<<<< HEAD
这是当前分支的内容
=======
这是另一个分支的内容
>>>>>>> feature
中间的等号把两个版本分开,Git要求你手动决定保留哪部分,还是都保留。解决方式:编辑文件,保留想要的内容,删掉<<<<<<<、=======、>>>>>>>这些标记,保存文件。然后执行:
bash复制git add 冲突文件
git commit # 这个提交就是“解决冲突”的提交
这里有个实用的建议:可合并的分支尽量频繁合并。分支存在时间越久,分叉越远,冲突可能性越大、解决成本越高。宁可每完成一个功能点就合一次,也不要攒一整个大功能再合并。
4.4 分支策略:什么样的项目用什么样的分支模型
分支虽好,但用不好也会乱。我自己用过最顺手的模型是Git Flow的简化版:
main分支:始终是可直接发布的稳定版本,禁止直接在上面提交代码,代码只能通过合并进来。develop分支:日常开发的主线,所有功能分支从这里切出去,也合并到这里。feature/xxx分支:具体功能分支,从develop切出,开发完成后合并回develop。hotfix/xxx分支:线上紧急修复分支,从main切出,修复完成后同时合并回main和develop。
新手可能会想:“就我一个人开发,搞这么多分支干嘛?”其实不然,即使单人项目,有个develop分支做开发缓冲,main始终只有可发布的版本,对养成良好工作习惯很有帮助。多人协作时这种模型的价值就更不用说了。
5. 远程协作:clone、push、pull和Pull Request
5.1 把本地仓库和远程仓库关联起来
当本地仓库建好之后,想把它推送到远程(GitHub、GitLab等平台),用:
bash复制git remote add origin git@github.com:用户名/仓库名.git
git branch -M main
git push -u origin main
第一条命令把远程仓库地址命名为origin(这是默认命名习惯);第二条把本地分支名设为main;第三条把main分支推送到远程,-u参数建立本地分支和远程分支的关联关系,以后直接git push就能推送。
如果是别人先建好了远程仓库,你想在此基础上开发,就克隆:
bash复制git clone git@github.com:用户名/仓库名.git
克隆下来的仓库自带origin远程地址配置,并且默认分支已经和远程建立对应关系,省去了前面关联的步骤。
5.2 日常同步:pull、push、fetch的区别
用Git协作时,你需要在本地和远程之间同步代码。三个相关命令容易搞混,我帮你理一下:
bash复制git fetch # 只把远程仓库的最新状态下载到本地,不改变工作区
git pull # 等于 fetch + merge,直接拉取并合并到当前分支
git push # 把本地提交推送到远程
这里建议刚开始用Git的人多用fetch,少用pull。原因是fetch不会动你的工作区,你可以先看看远程有什么变化,再决定怎么处理;pull则直接把远程代码合并进当前分支,如果和本地代码有冲突,就会直接进入冲突状态。我是这样操作的:每天开工第一件事git fetch看看远端有没有新提交,确认没影响再git pull。
5.3 多人协作中的典型工作流
标准的多人在同一个仓库上的协作流程是:
- 同步最新代码:
git pull或git fetch && git merge - 新建功能分支:
git checkout -b feature/login - 开发并提交:
git add . && git commit -m "feat: 实现登录功能" - 推送分支到远程:
git push -u origin feature/login - 在远程平台发起Pull Request(或Merge Request),请求代码审查
- 审查通过后合入主分支
这个流程的价值不只是“代码合并”,更重要的是代码审查。让另一个人在你代码合入主分支之前看一遍,能避免大量潜在问题。即使你一个人开发,也建议用这个流程,至少能看到自己在每个分支上做了什么。
5.4 一个常见场景:我和同事改了同一处代码
实战中最常见的冲突场景就是两个人都改了同一个文件的同一区域。举个例子:你和同事都在各自的feature分支上修改了config.js的第10行,你先把自己的分支合并进main,同事后合并时,Git就会报冲突。
这时候的处理思路不是“把对方代码删了”,而是先跟对方沟通一下,确认哪部分逻辑才是新的正确逻辑,然后手动编辑文件,把两边改动整合好,再提交。我见过不少新手在这一步直接git checkout --ours或者git checkout --theirs二选一,结果把对方的代码覆盖了,后面又花大量时间重新找补。记住:冲突是业务层面的问题,不是技术层面的问题,给它一点时间沟通清楚再动手。
6. 高频报错与排查技巧
6.1 场景一:代码提交后发现错了,怎么处理
提交历史中出现错误提交,大概是Git使用中最常见的需求场景之一。我按“错误程度”分几个级别说明:
轻微错误——提交信息写错了:
bash复制git commit --amend -m "新的提交信息"
这个命令会修改最近一次提交的信息。注意如果这个提交已经推送到远程,amend之后会出现本地分支和远程分支历史不一致的情况,处理起来就比较麻烦。所以建议只在提交还没推送时使用amend。
中等错误——少提交了一个文件(想把另一个文件追加进上个提交):
bash复制git add 遗漏的文件
git commit --amend --no-edit
--no-edit表示保留原来的提交信息,直接把新文件追加进去。
严重错误——提交了不该提交的内容(比如密码文件):
bash复制git reset HEAD~1 # 撤销提交但保留改动
# 然后把敏感内容从文件里删掉后重新提交
git add .
git commit -m "正确的提交"
如果错误提交的内容已经被推送到远程了,你需要非常谨慎。不要轻易用git push --force覆盖远程历史,这会影响其他人的仓库。正确做法是优先考虑用git revert——它生成一个新的反向提交来抵消之前的提交,不改变历史,也不影响别人:
bash复制git revert 提交哈希
6.2 场景二:误删文件或误改内容,怎么恢复
以我自己的经验,git checkout --和git restore是最容易让新手“心跳加速”的两个命令,因为它们会直接丢弃工作区的改动,且无法恢复。所以每次在执行这类操作之前,我会先做一次git stash把当前改动暂时存起来,确认没问题再清理:
bash复制git stash # 把当前所有未提交的改动暂存起来,工作区回到干净状态
git stash list # 查看暂存的列表
git stash pop # 恢复最近的暂存改动并删除暂存记录
这个技巧在实际工作中非常实用。比如你正在开发新功能,突然线上出了紧急bug,你又不想把改到一半的代码提交到仓库里。这时候git stash就派上了用场:改动了先存起来,把工作区清干净,切到hotfix分支修复完、提交、切回来,再git stash pop恢复之前的进度,无缝衔接。
6.3 场景三:push被拒绝,远程有本地没有的提交
当执行git push被拒绝时,十有八九是远程仓库有了新的提交,而你的本地分支没有包含它们。这种时候最忌“一根筋”地执行git push --force硬推,会把远程的提交覆盖掉。标准做法是:
bash复制git pull # 拉取远程提交,与本地合并,解决可能的冲突
git push
但如果本地和远程的提交历史出现分叉(比如你用git reset --hard改写了本地历史),此时git pull可能会产生大量冲突或无效合并。一种更清晰的思路是:
bash复制git fetch
git rebase origin/main
git push
rebase和merge的区别值得写一段说明:merge会生成一个合并提交,保留两条分支的交汇点;rebase则是把本地提交重新“嫁接”到远程最新提交之上,历史是一条直线,更干净。初次使用rebase前建议先备份一下分支,或者先在测试仓库里试几次,因为rebase会改写提交哈希,误操作后恢复起来更麻烦。
6.4 常见问题速查表
| 问题现象 | 原因 | 常用解决方案 |
|---|---|---|
Please tell me who you are |
未配置user.name或user.email | git config --global user.name/email |
| push被拒绝 | 远程有本地没有的提交 | git pull或git fetch + git rebase,再push |
| 切换分支时提示文件会被覆盖 | 当前分支有未提交改动,目标分支同名文件不同内容 | git stash 暂存改动后切分支 |
| 误提交了敏感信息 | 不小心把密码、密钥提交到仓库 | 立即git commit --amend或git reset,并修改对应的安全凭证 |
| 代码合并出现大段冲突 | 多人修改同一文件的同一个区域 | 手动合并冲突文件并沟通确认,再add+commit |
| 看不到远程刚创建的分支 | 本地引用没更新 | git fetch --prune 更新远程分支列表 |
这些问题是新手遇到最多的几类,把这几个场景吃透了,日常开发基本不会卡壳。
7. 实用技巧:让Git用起来更顺手
7.1 用一个文件让Git知道该忽略什么
.gitignore文件的作用是告诉Git“哪些文件不用管”。项目中的node_modules/、编译产物dist/、环境配置文件.env、IDE配置.idea/等都不应该被提交到仓库。没有.gitignore的话,git status会被大量无关文件淹没,提交历史也会被垃圾提交污染。
.gitignore的匹配规则很简单,每行一个模式:
code复制# 忽略node_modules目录
node_modules/
# 忽略所有.log文件
*.log
# 忽略build目录下的所有内容但不忽略build目录本身
build/
# 忽略.env文件,但不忽略.env.example
.env
!.env.example
写.gitignore时注意:如果一个文件已经被Git跟踪,再将其加入.gitignore是无效的。需要先把它从Git的跟踪中移除才行:
bash复制git rm --cached filename
--cached表示只从Git索引中移除,保留工作区的文件。这招在“文件不该被提交但已经提交了”的场景下非常实用。
7.2 别名:把高频命令变短
Git允许给命令起别名,这是一件能显著提升日常效率的小事。在~/.gitconfig里配置:
bash复制git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci "commit -m"
git config --global alias.lg "log --graph --pretty=format:'%h -%d %s (%cr) <%an>' --abbrev-commit"
配置之后,git st就等于git status,git lg会以图形化方式显示提交历史,信息密度很高。刚开始我以为是偷懒行为,用久了发现别名大大降低了操作成本——省掉的那几秒虽然微不足道,但减少了“我下一步该打什么命令”的思维中断。
7.3 提交信息规范:不只是给别人看的
关于提交信息,有一种常见误解是“只要自己看得懂就行”。但实际情况是,Git提交历史是一份“项目时间线”,你未来三个月后回头看,甚至同事离职后接手你的代码时,都要靠提交信息来理解项目的演进。所以从第一天起养成写规范提交信息的习惯,长期来看是很值得的。
推荐的提交信息格式:
code复制<type>(<scope>): <subject>
<body>
type包含:
| Type | 用途 |
|---|---|
| feat | 新功能 |
| fix | 修复bug |
| docs | 文档修改 |
| style | 代码格式调整 |
| refactor | 重构,功能不变 |
| test | 添加测试 |
| chore | 构建工具、依赖等杂项 |
subject部分用一句话概括本次提交,用祈使句,不超过50个字符,不要以句号结尾。比如“添加注册页面的表单验证”就好过“验证写了”或者“update”。如果改动内容较多,在subject下面空一行写body,具体说明改动的原因和影响范围。
写在最后:我的几个使用习惯
最后再分享几个我从实际使用中沉淀下来的习惯。第一,提交前必看git diff——git diff --stat能看改了哪些文件,git diff能看具体内容,确认没有把调试代码、日志输出、无用注释提交进去。第二,每次提交只做一件事,宁可多提交几次,也不要一次提交十个文件改完十个需求,这样回滚历史时才能精确定位。第三,遇到不确定的命令就先在测试仓库里试一遍,我专门建了一个叫test-git的仓库用来练手,任何不确定的操作都先在里面跑一遍,再放到真实项目上执行。
Git的复杂度在工具类软件里算中等偏上,但它跟那些需要“背命令”的东西不同,它的命令逻辑都围绕着“三个区域+分支+远程”这套核心模型。把这套模型内化之后,你会发现那些五花八门的命令根本不需要背,遇到场景自然就知道该用什么。希望这篇能帮你把Git这条路走顺,少踩几个我当年踩过的坑。
