
Git这个工具只要是写代码的人早晚都得面对它。不管你是刚进公司的实习生还是自己折腾开源项目的独立开发者都绕不开这六个字母。今天这篇东西我不打算长篇大论地讲Git的内部实现原理那玩意见官方文档就行我更想从实际干活的角度把从安装、配置、日常提交、分支合并到远程协作、SSH认证失败排查这一整条链路给你捋明白。你可以把它当成一份能直接照着抄作业的操作手册也可以当成一份避坑清单来用。先说清楚这篇到底覆盖什么Git是什么、为什么要用Windows和Mac上怎么装装完之后必做的身份配置日常最常用的add、commit、status、log、diff这些命令怎么配合分支到底怎么建、怎么切、怎么合并、冲突怎么解远程仓库的clone、push、pull、fetch之间有什么区别以及很多人第一个月就会被卡住的SSH认证失败到底怎么排查。看完这篇文章你不需要背任何命令只需要理解每个动作在解决什么问题剩下的交给肌肉记忆。1. 先搞清楚Git到底在解决什么问题1.1 Git是版本管理工具不是网盘很多人第一次接触Git容易把它跟“网盘”“同步盘”搞混。网盘的逻辑是我把文件传上去你在另一台设备下载下来两边数据尽量保持一致。Git的逻辑完全不同它的核心是版本快照——你每提交一次Git就把当前所有文件的状态拍一张“照片”存下来这张照片里不仅包含文件内容还包含这次提交的作者、时间、说明信息以及和上一张照片之间的前后关系。这个设计解决了一个很实际的痛点你在改代码、写文档、做设计稿的时候经常会遇到“我把上一版改坏了得退回之前那个能用的版本”的情况。没有Git的时候大家惯用的做法是手动复制一份带上日期后缀的文件夹项目_最终版、项目_真最终版、项目_绝对不改了。这种命名方式短期内能忍一旦项目超过两周、参与的人超过两个你根本记不住哪个是哪版更说不清楚谁在什么时候改了哪一行。Git把这一切变成了结构化的记录。你每一次提交都有一个唯一的版本号哈希值可以随时回退到任何一次提交可以对比任意两个版本之间到底改了什么还可以在同一个项目里并行开多条“时间线”互不干扰。这就是它和网盘最本质的区别网盘关注“现在有什么”Git关注的是“怎么一步一步变成现在这样的”。1.2 使用Git前后开发体验差在哪我举一个特别常见的场景。你在一个项目里同时要改两个功能一个是紧急的线上Bug一个是下周才需要的新特性。没有Git的时候你只能在同一个代码目录里改来改去经常出现“修Bug修到一半被叫去写新功能改完新功能回来发现自己忘了Bug改到哪儿了”的窘境。有Git之后你先在新特性这条分支branch上写代码写到一半要修Bug提交或暂存当前进度切回主分支新建一条修Bug的分支修完合并回去再切回新特性分支继续写。所有代码都待在它们该待的地方互不干扰。这种感觉用一次就回不去了。再说团队协作。几个人同时在一个项目里干活如果没有版本管理最常见的死法就是“互相覆盖文件”A同学改了登录页B同学也改了登录页最后上传的时候看运气谁后上传谁赢。Git通过“分支合并”机制接受多人同时修改然后在你合并代码的时候明确指出哪些地方产生了冲突由人工来决定每一处冲突保留谁的内容。这个机制本身就是现代软件工程能够多人并行开发的基础。2. 装环境配身份从零开始把Git跑起来2.1 Windows和macOS下的安装方式Windows下最简单的方式是直接下载官方安装包。官网下载页面提供64位和32位两个版本现在绝大多数机器都是64位选带“64-bit”字样的那个就行。安装过程基本全程Next但有几个选项值得注意安装到“Select Components”这一步时建议勾选“Add a Git Bash Profile to Windows Terminal”如果选项里能看到这样后续可以用终端工具直接打开Git Bash在“Choosing the default editor”这一步新手建议保持默认的Vim不要换哪怕你觉得Vim反人类等以后熟悉了再说因为很多教程和命令行的默认操作都假设它没被动过在“Adjusting your PATH environment”这一步务必保持默认选项“Git from the command line and also from 3rd-party software”这个选项保证你在CMD和PowerShell里也能直接用git命令。装完之后检查是否成功打开任意终端输入git --version能输出版本号类似git version 2.47.1.windows.1就说明安装成功了。这一步虽然简单但很多人装完不知道去哪验证以为装完必须看到桌面快捷方式才算成功其实Git是没有图形界面的它的快捷方式是右键菜单里的“Git Bash Here”。macOS的情况要取决于你的机器状态。最省事的方式是安装Xcode Command Line Tools在终端里输入xcode-select --install系统会弹窗引导安装装完之后Git通常就已经可用了。如果你需要更新版本的Git可以考虑用Homebrew安装brew install git。这里要留意一个细节macOS自带的git版本可能比较老跟你在项目里用到的某些Git特性不兼容所以如果你靠Homebrew装了新版要注意PATH优先级确保which git指向的是Homebrew的版本而不是系统的旧版。还有一个容易忽视的大坑很多Windows用户装完Git之后不会用Git Bash反而跑到CMD里去敲Linux风格的命令。比如mkdir -p a/b这种命令在CMD里是跑不通的。我的建议是Windows用户凡是涉及Git操作一律右键打开Git Bash Here它模拟了Linux终端环境几乎所有教程里的命令都能直接跑。2.2 安装完必须先配置身份Git的每一次提交都会记录作者信息这个信息不是系统自动从你的电脑里读取的而是来自一个全局配置文件。如果不配置提交时会报错或者使用占位的错误信息。配置方法很简单git config --global user.name 你的名字 git config --global user.email 你的邮箱这里有几个值得展开的点。第一--global参数表示当前操作系统用户范围内的全局配置通常一台开发机上所有项目的作者信息都是同一个所以用global是合理的。如果某个特殊项目要用不同的名字和邮箱可以在项目目录里不加--global单独设置那个项目的配置会覆盖全局配置。第二邮箱不一定要用真实邮箱但建议使用你在代码托管平台注册的邮箱。因为很多平台就是靠email跟你的账号关联的如果你的提交邮箱跟平台账号不一致提交记录虽然能推上去但头像和用户名不会正确显示甚至会被算成另一个人的贡献。这个问题在团队里经常被忽略最后统计KPI的时候才发现一堆提交没落到自己头上。配置完之后你可以用git config --list查看所有配置项确认没问题再开始建仓库。2.3 配置SSH密钥免掉每次输入密码的麻烦日常操作远程仓库有HTTPS和SSH两种协议。HTTPS每次推拉代码都要输入账号密码或者在凭据管理器里记住密码SSH则通过公钥私钥配对实现免密认证。很多新手在第一次面对远程仓库时直接选HTTPS结果每次push都输密码输到崩溃还有一批人想配SSH结果一上来就遇到“SSH认证失败”直接卡在第一关。配置SSH的标准流程是这样的。先检查本机是否已有SSH密钥ls -al ~/.ssh如果目录下有id_rsa和id_rsa.pub这两个文件说明你之前生成过密钥可以跳过生成步骤如果没有执行ssh-keygen -t rsa -b 4096 -C 你的邮箱一路回车即可默认会在~/.ssh目录下生成一对密钥。id_rsa.pub是公钥可以公开把它的内容复制出来粘贴到你使用的代码托管平台的“SSH Keys”设置页面里。id_rsa是私钥绝对不能泄露给任何人它相当于你访问远程仓库的身份证。验证配置是否成功在Git Bash里执行ssh -T gitgithub.com如果看到类似“Hi xxx! Youve successfully authenticated”的话说明SSH链路已经通了。这里要特别强调一点SSH认证连接的是平台不是具体项目仓库所以你只需要成功认证一次后续访问该平台下所有仓库都不需要再输入密码。还有一类常见情况是公司自建的GitLab端口可能不是默认的22需要在~/.ssh/config里单独配置Host和Port这个我们后面在问题排查部分展开。3. 日常高频命令从初始化仓库到看懂历史记录3.1 创建一个新仓库并完成第一次提交有了Git之后你随时可以把任意一个本地文件夹变成Git仓库。在项目根目录执行git init执行之后该目录下会多出一个隐藏的.git文件夹这个文件夹装着所有版本历史和配置信息属于Git的“数据库”。这个时刻你可能会好奇我怎么知道Git现在到底处于什么状态用git statusgit status它会告诉你当前分支名默认通常是master或main、有哪些文件还没被跟踪、有哪些文件修改了还没提交。新手最常见的困惑是我明明往项目里放了个文件怎么git status说它是“Untracked”这个状态的意思是Git发现了这个文件但还没决定要不要把它纳入版本管理。你得先告诉Git“我要管这个文件”执行git add 文件名再执行git status文件从“Untracked”变成了“Changes to be committed”意思是它已经被放进了“暂存区”。这时候就可以提交了git commit -m 初始化项目添加了README到这里你的项目就有了第一个版本快照。很多人不理解为什么要分“add”和“commit”两步其实这个设计非常实用。你可以把add理解为“挑菜”——从一堆改动里选出你这次想记录的内容把commit理解为“拍照”——把选好的内容打包成一条不可变的历史记录。这一步拆开之后你可以同一个文件改两处只提交其中一处这在复杂的工作流里非常关键。3.2 用生活化类比理解工作区、暂存区、版本库如果不用术语可以这样想工作区就是你的编辑器里正在编辑的文件暂存区是一个“待提交清单”你点一下git add就相当于在清单上打了个勾版本库就是你Git仓库的历史档案柜每commit一次就把当前清单上的内容封锁成一个档案存进去永不可变。这三个区域的转换是理解Git的命门。在工作中你会反复遇到的情况是改了一堆文件看了git status发现有些改动想提交有些改动还不想提交这时git add就提供了精准控制能力——只把想提交的文件加入暂存区其他改动留在工作区继续“挂机”。想撤销误add的文件怎么办git reset HEAD 文件名想撤销工作区里某个文件的修改、恢复到最近一次提交的状态怎么办git checkout -- 文件名这两个命令属于“后悔药”但要注意它们都不可轻易乱用。尤其git checkout -- 文件名会直接丢弃工作区的修改而且改动不会进回收站找不回来的。如果你不确定那些改动是否还要建议先git stash暂存起来而不是直接撤销。3.3 查看历史与对比差异status、log、diff三兄弟三个命令分别解决三个问题。git status回答“现在处于什么状态”git log回答“过去发生了什么”git diff回答“到底改了什么”。git log的输出初看很吓人一长串哈希值加提交信息git log --oneline这个命令把每次提交压缩成一行只显示简短的哈希前缀和提交说明这是日常查看历史最常用的方式。如果你觉得提交历史太多太乱可以加参数过滤比如git log --oneline -5只看最近5条git log --oneline --author你的名字只看某人的提交。git diff是排查问题的利器。它默认比较“工作区”和“暂存区”之间的差异说白了就是你改了但还没add的内容具体改了哪些行。执行git diff输出里带-号的是删除或修改前的行带号的是新增或修改后的行。如果你已经git add了想看看暂存区跟最近一次提交的差异用git diff --cached。这个命令在你准备提交之前一定要养成习惯跑一遍能避免把调试用的临时代码提交到正式仓库。4. 分支管理并行开发的基石4.1 分支是什么为什么这么设计分支branch大概是Git区别于老式版本控制系统的标志性设计。你可以这样理解主分支是一条主干道分支是从主干道上分出来的岔路。你在岔路上怎么折腾都不影响主干道折腾完了可以把岔路合并回主干道也可以直接丢在一边不管。创建分支和切换分支是两个动作git branch feature-login git checkout feature-login第一个命令创建分支但还不切换第二个命令切换过去。Git后来提供了一条合成命令git checkout -b feature-login它的意思是“创建并切换”日常使用频率极高。在老版本里checkout干的活特别杂既能切分支又能撤销文件后来Git团队推出了语义更清晰的switch命令git switch -c feature-login功能跟checkout -b一样。现在新项目里推荐用switch来处理分支切换语义清楚不容易跟撤销文件搞混。分支名取什么也很讲究。我见过的优秀习惯是用类型前缀feature/xxx表示新功能fix/xxx表示修Bugdocs/xxx表示文档更新。这个命名习惯在后来的协作中会大大降低沟通成本你一看分支名就知道这条分支是干嘛的。4.2 合并分支的两种思路merge和rebase分支开发完后要把成果带回主分支最常见的是merge合并。切回主分支后执行git merge feature-loginGit会把两个分支的修改合并到一起。如果两边改的是不同文件Git会智能地自动合并不需要你做任何事如果两边改的是同一个文件的同一行就会产生合并冲突需要手动处理。这里新手常遇到一个心理坎明明只是把分支合并到主分支为什么git log里出现了一条“Merge branch xxx”的记录这是merge的正常且合理的表现——它忠实记录了“在什么时间点把哪条分支并了进来”这个事实。有强迫症的人会觉得历史不够线性整洁于是想用rebase来让历史像一条直线。rebase的原理是把当前分支的提交摘下来接到另一个分支的最新提交之后相当于“重新放一遍”你的提交。比如你在feature分支上有三个提交主分支在这期间新增了一个提交rebase会把你的三个提交按顺序接到主分支最新提交的后面看起来像你是在主分支最新状态下依次做了三个修改历史非常线性。git checkout feature-login git rebase main但rebase有一个危险点如果这个分支是多人协作共用的已经在远程被别人的代码依赖了你rebase会改变提交历史因为提交的哈希变了推送出去会引发一堆混乱。我的原则很简单还没推到远程的本地分支随便rebase已经推到远程和别人协作的分支老老实实用merge。4.3 合并冲突的真实现场与解决步骤冲突是每个用Git的人迟早要面对的它不是错误而是Git在明确告诉你“我没法替你决定需要你出面裁决”。冲突发生时git status会列出哪些文件是“both modified”状态。打开冲突文件你会看到类似这样的标记 HEAD 这是当前分支的代码 这是被合并分支的代码 feature-login这个标记不是代码是冲突的提示。到之间是当前分支的内容到之间是你要合入分支的内容。你需要手动决定保留哪个、删除哪个或者混合修改。处理完之后删掉那些标记行然后把这个文件git add再git commit冲突就正式解决了。我在处理冲突时有个经验很管用先看几处冲突的上下文不要急着逐个改。有时候多个冲突分布在同一个文件的逻辑关联位置单个看可能觉得两边都有道理但整体看你会发现一个方案明显是对的。如果冲突数量巨大优先用图形化工具比如VS Code的Source Control面板、IDEA的Merge窗口它们会把左右两边的内容并排展示比在编辑器里看标记符号直观得多。还有一个实战建议合并前先拉一下最新代码。如果你在主分支上执行merge之前主分支本身已经落后远端那合并出来的结果大概率会产生莫名其妙的冲突甚至引入别人的改动问题。先把主分支更新到跟远端一致再合并本地特性分支冲突范围会小很多。5. 远程仓库协作代码如何与他人同步5.1 clone、push、pull、fetch到底什么区别本地仓库玩熟之后就要面对远程仓库了。远程仓库的本质是一台不断线的服务器它存着所有版本历史和当前代码大家各自往它上面“取”或“交”代码。clone是把远程仓库完整复制到本地。一个经典问题“我要怎么把别人的项目拿到本地”答案是git clone 仓库地址。执行后本地会生成一个与远程同名的文件夹而且这个文件夹里自动配置好了远程仓库地址可以直接push、pull。如果只需要克隆某个分支可以加-b 分支名参数。push是把本地提交推送到远程。注意如果你本地有3个提交远程一个都没有执行git push后远程会依次出现这3个提交。推送失败最常见的原因是远程有本地没有的提交Git会拒收提示你“Your branch is ahead of...”。这时候你不能硬推得先把远程的最新提交拉下来或者用git pull --rebase把本地提交叠加到远程最新之后再推上去。pull是fetch merge的组合动作。很多人搞不清它和fetch的区别其实fetch只是“下载远程的最新提交到本地”但不会自动修改你正在工作的文件pull则会把这些提交合并到当前分支直接改变工作区。对新手来说直接理解成“pull 获取远端最新代码并合到当前分支”就够了。想预览远程有没有新提交、但还不想动自己的代码就执行git fetch。fetch和pull的选择我在实操里的建议是单人开发时直接git pull最省心在多人并行开发、你手头有一堆未提交改动时先git fetch看看远程有哪些新提交再判断是merge还是rebase更安全。5.2 完整协作场景演练新成员拉取项目并提交代码假设你刚加入一个项目同事给了你一个仓库地址。完整流程是克隆仓库git clone gitcode.platform.com:team/project.git进入项目目录cd project查看当前分支git branch -a看看有哪些远程分支基于远程分支创建自己的本地分支git checkout -b feature/xxx origin/xxx注意新分支要关联远程分支之后push才知道推到哪开发完提交git add .git commit -m 完成了xxx功能推送git push -u origin feature/xxx-u建立远程追踪关系第一次推送后以后直接git push就行很多人第一次操作时会漏掉第4步里关联远程分支这个环节结果git push之后提示git push --set-upstream origin feature/xxx。这是Git在告诉你“我暂时不知道你要推到远程哪条分支”按提示执行--set-upstream即可不需要慌。还有一个小细节在提交前养成看git status和git diff的习惯确保添加的就是你想提交的内容别把本地调试用的垃圾文件一起推上去。团队协作的底线是不要把本地私有信息提交上去。比如配置文件里含数据库密码、密钥这类内容一旦推到远程即使后续删掉也会留存于Git历史里重新修复成本极高。这个问题严重的时候甚至需要改写历史非常麻烦。5.3 主分支命名与常见协作模型远程仓库的主分支老项目通常叫master新项目很多改叫main。创建仓库的时候平台一般默认帮你建好主分支了本地git init时则默认是master如果你希望改成maingit branch -M main-M是强制改名的意思大写M表示“不管目标分支存不存在直接改”。Git允许本地主分支名和远程不一致但团队协作时统一命名能省掉很多不必要的困扰。常见的协作模型有三种。最简单的是单主分支模型所有人都在main上直接提交适合一两人的小项目稍微规范一点的是功能分支模型每个人开一条feature/xxx分支完成后合回main适合三五个人的项目再正式一点的是Git Flow模型维护main、develop、release、hotfix等多条常设分支适合发布节奏严格的中大型项目。新手不用一上来就背模型先掌握“主分支功能分支”就够了等你真的到了项目需要的阶段自然而然会去学。6. 高频报错与排查实战别让第一个月卡死在环境上6.1 SSH认证失败最常见的拦路虎“ssh认证失败 git”在热搜词里挂着不是没道理的我见过太多人在配好密钥之后被这个报错折磨。先搞清楚这个错误出现的两种场景一是clone或push时报错Permission denied (publickey)二是ssh -T gitxxxx时直接显示Permission denied。排查思路按以下顺序走第一步确认本机是否生成了密钥。执行ls -al ~/.ssh看有没有id_rsa.pub。没有就重新生成生成时注意它会问你“Enter file in which to save the key”直接回车使用默认路径。第二步确认公钥是否已添加到托管平台。复制id_rsa.pub的全文内容不要多复制换行符粘贴到平台个人设置里的SSH Keys页面。有些平台要求标题随便起个能标识设备的名字即可。第三步确认本机SSH正在使用哪个私钥。执行ssh -vT gitxxxx.com加-v参数进入调试模式它会打印详细连接过程你可以从日志里看到它尝试了哪个私钥文件、有没有被接受。如果它尝试的私钥跟你实际在用的私钥不是同一个需要在~/.ssh/config里指定Host code.platform.com HostName code.platform.com User git IdentityFile ~/.ssh/id_rsa Port 22第四步检查系统时间和密钥权限。这个问题比较少但真实存在电脑系统时间不正导致SSH证书验证失败~/.ssh密钥文件的权限太开放也会被拒绝特别是Windows上配置多用户时建议对私钥文件执行chmod 600 ~/.ssh/id_rsa、对目录执行chmod 700 ~/.ssh。我在这个环节还有一个建议办公环境下公司的GitLab可能配了自定义端口比如2222你在clone地址里会看到ssh://gitcode.company.com:2222/group/project.git这种形式。这种地址如果直接配SSH默认端口匹配不上就会失败必须在~/.ssh/config里把Port写成2222。这是很多人反复失败又找不到原因的高频坑。6.2 其他高频报错速查fatal: Not a git repository在不是Git仓库的目录里执行了Git命令。如果你确实在项目目录里可能是.git文件夹被删了或者你不在项目根目录而在某个子文件夹但该文件夹没有被Git跟踪通常子目录在Git仓库内是能用的。最简单的验证方式是git rev-parse --git-dir它能输出Git仓库的路径如果报错说明当前不在仓库内。Please tell me who you are忘了配user.name和user.email回到第2.2节配置全局身份即可。注意如果这个项目是用系统用户安装的Git而你的终端是管理员权限开的配置可能会写进不同的用户目录导致“我明明配了还是报错”这时候用git config --list确认配置在哪个层级生效。fatal: refusing to merge unrelated histories两个仓库没有共同的提交祖先最常见于本地新建项目后用git remote add origin连接了远程仓库然后直接git pullGit默认拒绝合并这种没有关联的历史。如果你明确知道两边内容一致或者确认可以合并命令里加--allow-unrelated-historiesgit pull origin main --allow-unrelated-histories我一般不推荐随手用这个参数它会让Git把两边各自的历史拼接起来可能产生大量冲突先确认你的操作意图再使用。error: failed to push some refs to ...远程有本地没有的新提交。不要用git push -f强推解决在团队项目里强制推送会覆盖其他人的提交属于高危操作。正确做法是git pull --rebase把远程新提交拉下来让本地提交叠加到最上方然后再push。warning: LF will be replaced by CRLFWindows和Linux换行符差异导致的警告。这类警告通常不会阻断操作但如果团队跨平台协作建议在项目根目录放一个.gitattributes文件统一行尾规则避免后续出现“明明没改内容却显示整文件都有改动”的诡异情况。6.3 几个能提升体验的实测小技巧git stash是移动的后悔药。你在一半的工作区改动还没写完时需要紧急切到另一分支处理事情直接切分支会带着未提交的改动过去容易混乱。执行git stash可以把当前改动临时收起来工作区回到干净状态切分支做完事回来执行git stash pop把改动恢复。这个命令我几乎每个星期都用属于投入产出比极高的操作。配置常用别名。给高频命令起短名字能显著提升效率我目前最常用的两组git config --global alias.lg log --oneline --graph --all用一个git lg就能查看图形化的提交历史git config --global alias.co checkout把checkout缩成co。这类配置纯属个人习惯但在频繁操作时非常省时间。提交信息用动词开头。写git commit -m fix: 修复登录超时问题而不是git commit -m 修复了登录超时问题。前者描述动作后者描述状态很多团队规范也会要求提交信息遵循特定格式养成好习惯以后去任何团队都受用。遇到不确定的命令时先查帮助。git help 命令名会打开本地完整文档git 命令名 -h会输出简版帮助。很多人觉得出了报错才去搜索其实先把常用命令的-h看一遍能减少大半的无效搜索时间。我自己刚接触Git那阵子栽得最多的就是SSH认证和合并冲突这两件事。SSH配好了之后推送成功率提升心里踏实一大截合并冲突处理过两三次之后再看到那些标记就不会慌反而会觉得“哦Git又在明确告诉我到底哪儿有分歧了”。Git这个东西其实不复杂它的每一个设计都在解决一个真实世界里的协作或者版本管理痛点你顺着“这个命令在解决什么问题”这个思路去学就没什么难的了。把上面这些基础练熟日常开发已经够用剩下的高级功能等真遇到特定场景再去查效率会高很多。