1. 环境准备:Git安装与初始配置
不管你用的是Windows、macOS还是Linux,装Git永远是第一步。很多人觉得这步简单得没必要单独拿出来讲,但在我见过的大量问题里,至少有三成是卡在最基础的安装和配置上——尤其是环境变量、换行符处理、默认编辑器这些看似无关紧要的选项,后面都会变成实打实的坑。
1.1 各平台安装方式与版本选择
Windows平台直接去Git官网下载安装包,选自己系统对应的64位版本即可。安装过程中有几个选项需要留意,建议按下面的方式选:
- Select Components:默认勾选的项目保持不动,额外建议勾上“Add a Git Bash Profile to Windows Terminal”。Windows Terminal现在基本是标配了,这个选项可以让Git Bash出现在终端下拉菜单里,用起来方便很多。
- Default Editor:如果你装了VS Code,这里直接选“Use Visual Studio Code as Git's default editor”,没装就用默认的Vim。我强烈建议选VS Code,因为后面解决合并冲突时,可视化编辑器比在终端里折腾Vim舒服不止一个档次。
- Adjusting your PATH environment:一定要选“Git from the command line and also from 3rd-party software”。选这个会把Git装进系统PATH,之后在CMD和PowerShell里也能直接用
git命令,而不是只能在Git Bash里用。 - Line Ending Conversions:这个选项非常重要,团队协作场景下建议选第一项“Checkout Windows-style, commit Unix-style line endings”。简单说就是Git在提交时自动把CRLF转成LF,拉取到Windows时转回CRLF。如果你的团队里混着Windows和macOS/Linux同事,选这个能避免大量“明明没改文件却显示一堆差异”的诡异情况。
macOS用户建议优先用Homebrew安装:brew install git,这样装出来的版本通常比系统自带的旧版本新很多,也方便后续升级。Linux(Debian/Ubuntu系)就用sudo apt install git,装完先git --version确认一下版本号,2.30以上都算比较新的,功能上不会有明显缺失。
安装完成的标准是:在终端里输入git --version能正常输出版本号。如果提示找不到命令,优先检查PATH有没有包含Git的安装目录,Windows下检查C:\Program Files\Git\cmd这个路径是否存在于系统环境变量中。
1.2 初始配置到底配什么——user.name和user.email不是随便填的
装完Git之后,第一件事不是急着拉仓库,而是配置身份信息。这一步跳过的人,基本都会在第一次git commit时收到一串红色报错,提示Please tell me who you are。
配置身份的核心命令就两条:
bash复制git config --global user.name "你的名字"
git config --global user.email "你的邮箱"
这里有个关键点值得多说两句:提交记录里的作者信息是永久的。以后别人用git log翻提交历史,看到的就是这个名字和邮箱。后续如果换公司、换邮箱,或者发现自己名字拼错了,即便改了配置,历史记录里那些旧信息也不会自动更新。所以配置之前想清楚,这个邮箱最好绑定到你的代码托管平台账户上,这样提交才能正确关联到你的账号头像和贡献记录。
除了身份信息,我建议顺手再配两条:
bash复制git config --global init.defaultBranch main
git config --global pull.rebase false
第一条把默认分支名从master改成main,这是当前社区的主流趋势,避免以后频繁创建仓库时每次都出现master分支。第二条设置git pull的默认行为是合并而不是变基,对于大多数人的日常习惯来说,merge更直观,不容易搞出意外。如果你已经知道rebase是什么并且明确想用它,再单独改也行。
1.3 配置的优先级与查看方式
还有不少人会困惑:为什么我明明配置了名字,某个仓库里提交的作者还是不对?这里涉及Git配置的三层优先级:
- 系统级:存储在Git安装目录,对所有用户生效,优先级最低。
- 全局级:存储在用户主目录下的
.gitconfig文件,对当前用户的所有仓库生效,日常配置写在这一层。 - 仓库级:存储在具体仓库的
.git/config文件,只对当前仓库生效,优先级最高。
所以,如果你在某个仓库内部执行过git config user.name "另一个名字"(不带--global),那么在这个仓库里提交时,用的是仓库级配置,而不是全局的。这是个很隐蔽的坑——排查“为什么提交者名字不对”时,先跑这条命令看看当前生效状态:
bash复制git config --list
输出结果里,后面出现的项目会覆盖前面的同名项目。用git config user.name单独查看某个配置项当前值也行。我见过好几个同事,明明全局配置是对的,某个老项目提交时作者却变成一串乱码或旧名字,查了半天才发现是当年在该仓库下配过局部覆盖。遇到这种情况,直接删掉仓库级配置即可:
bash复制git config --unset user.name
git config --unset user.email
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心命令拆解:从克隆到提交的完整链路
配置做完,接下来就是日常使用频率最高的核心操作链路:克隆、修改、暂存、提交、推送。这一节不打算单纯罗列命令,我更想把这个链路背后的状态模型讲清楚。一旦你理解了Git的三个状态区域,几乎所有基础操作的逻辑都会自动串起来。
2.1 克隆仓库:HTTPS与SSH的选择逻辑
从远程仓库获取代码,主流方式有两种地址:HTTPS和SSH。选择哪一种,直接决定你后续推送代码时要不要反复输密码。
HTTPS方式的优点是零配置,第一次克隆时输入账号密码就能拉取代码。缺点是推送时基本都要重新验证身份,除非你在系统里配置了凭证管理器(Windows的Git Credential Manager、macOS的钥匙串)。适合偶尔使用Git、或者在公司内网环境里已经配好单点登录的场合。
SSH方式的优点是配置一次密钥后,后续所有推送和拉取都免密执行,体验非常顺滑。缺点是首次配置稍微麻烦那么几分钟。我个人在所有长期开发的项目上基本都是SSH,这也是推荐给所有需要每天和代码打交道的人的方式。
克隆仓库的命令格式很简单:
bash复制git clone git@github.com:someorg/somerepo.git
也可以克隆到指定目录:
bash复制git clone git@github.com:someorg/somerepo.git my-project
执行完git clone后,会自动把远程地址记录在origin这个别名下,并且本地自动创建一个和远程默认分支同名的分支,同时建立跟踪关系。这也是为什么克隆完成后直接git status就能看到Your branch is up to date with 'origin/main'这样的提示。
2.2 工作区、暂存区、版本库——提交前必须搞清的状态模型
很多初学者最容易混的一个概念就是:git add到底做了什么,为什么它和git commit是两步而不是一步?
这里用一个购物车的类比来解释。工作区就是你的购物车所在超市,你可以随意往车里放东西、拿东西出来,什么都不影响。暂存区是收银台前的结账区,你git add就是把商品从购物车搬到结账台上,这些商品已经确定了“本次要买单”的身份。版本库就是你已经付完款、装进袋子里带回家的东西,对应git commit之后形成的一个不可变快照。
这个“先add再commit”的设计不是多此一举,它的意义在于:你可以在一次提交里只挑选部分文件参与。比如你同时改了三个文件,但其中两个功能相关、一个只是调试用的临时改动,那你完全可以只把前两个git add进去提交,临时改动留在工作区里继续观察。这在项目实践中非常常见。
常用命令对应如下:
bash复制git status # 查看当前状态
git add filename.txt # 暂存指定文件
git add . # 暂存所有改动(慎用,建议先看status)
git commit -m "提交说明" # 把暂存区内容生成一个提交
git log --oneline # 查看提交历史
有个使用细节值得特别注意:git add .在项目比较混乱时很容易把不想提交的文件(比如本地的配置文件、临时调试代码)一并暂存进去。所以我个人的习惯是:提交前一定先跑git status看清楚有哪些改动,再用git add精确添加文件。
2.3 提交信息规范与commit的实操细节
git commit -m "提交说明"是每个人都会打的一条命令,但提交信息的质量往往天差地别。我见过最让人崩溃的提交信息就是“update”“fix”“1”——等三个月后再回头查某个改动为什么引入问题时,这种提交信息基本等于没有信息。
推荐一个实用且不复杂的格式,适用于绝大多数团队的日常节奏。用一句话概括改动类型+改动内容,比如:
text复制fix: 修复登录状态下token过期未跳转的问题
feat: 新增用户导出功能
docs: 更新README中的部署说明
refactor: 重构订单状态机的判断逻辑
这种风格参考了社区流行的Conventional Commits规范,但不是让你死板照搬。核心逻辑就一条:让未来的人(包括三个月后的你自己)只看提交信息就能明白这次改动的意图。
另一个实用命令是git commit --amend。如果你想修正最后一次提交的说明文字,或者发现最后一次提交漏了一个文件,可以这样操作:
bash复制git add forgotten-file.txt
git commit --amend --no-edit
--no-edit表示保持原提交信息不变,只把新增文件并入上一次提交。注意,--amend的本质是生成一个新提交替换掉旧的提交,所以如果这个提交已经推送到了远程,你amend之后再push就会被拒绝(除非强制推送)。已经推送过的提交,不要随便amend,否则会直接影响队友的历史记录。
2.4 .gitignore的常见配置
.gitignore文件的本质很简单:告诉Git哪些路径不该被纳入版本管理。但很多人是项目跑起来之后才补这个文件,结果发现某些文件已经被跟踪了,加了规则也不生效。
正确姿势分两步:先确认没有提交过不该提交的文件,然后在仓库根目录创建.gitignore。常见需要忽略的内容包括:
- 依赖目录:
node_modules/、vendor/ - 构建产物:
dist/、build/ - 环境配置:
.env、config.local.js - 系统文件:
.DS_Store、Thumbs.db - IDE配置:
.idea/、.vscode/(部分团队会选择提交共享的IDE配置,可以按团队约定来)
一个容易被忽略的点是:.gitignore只对未跟踪的文件生效。如果你之前已经把node_modules提交到了仓库里,现在在.gitignore里加上node_modules/并不会让它从仓库中消失。这时候需要额外的操作:
bash复制git rm -r --cached node_modules
git commit -m "chore: 停止跟踪node_modules目录"
--cached参数的意思是只从Git的索引(版本库记录)中移除,但保留工作区的文件。这样既能从仓库里去掉依赖目录,又不用真的删除本地文件,两全其美。
3. 分支管理与合并:多人协作的命脉
如果说前面这些操作是单机版的Git使用,那么分支就是进入团队协作世界的门票。分支管理用得好,项目再多人并行开发也不会互相踩脚;用不好,就是每天活在冲突和混乱里。
3.1 为什么要用分支——问题的本质
分支解决的经典场景是这类:你和同事同时在同一个项目上开发两个功能,功能A需要大改公共模块,功能B只需要加一个独立页面。如果在同一个分支上干活,同事拉取代码时就会看到你做了一半的功能A代码,可能压根跑不起来。
有了分支之后,每个人都可以基于一个稳定的基点创建自己的开发线。你在自己的分支上随便折腾,没做完也不会影响其他人。
举个例子,项目默认有个main分支,所有人约定这个分支上的代码必须保持可运行状态。你要开发新功能,先基于main创建自己的分支:
bash复制git branch feature/login-page # 创建分支
git checkout feature/login-page # 切换到该分支
这两条可以合并为一条更常用的命令:
bash复制git checkout -b feature/login-page
2022年之后的Git版本也支持用git switch -c feature/login-page,语义上更清晰,专门用于切换分支,避免了checkout一个命令承担多职责的混乱。两条命令都行,选一个自己顺手的就好。
3.2 创建、切换、删除分支的常用操作
分支的日常操作无外乎创建、切换、查看、删除和推送。逐个过一遍这里面的实用技函数。
查看分支:
bash复制git branch # 本地分支列表,当前分支前有*号
git branch -a # 本地加远程全部分支
git branch -vv # 查看本地分支与远程分支的跟踪关系
删除分支:
bash复制git branch -d feature/old-page # 删除已合并的分支
git branch -D feature/old-page # 强制删除未合并的分支
-d和-D的区别必须清楚:如果分支上有尚未合并的提交,-d会拒绝删除,这是安全机制;-D则是强行删除,这些提交会变成“无主提交”,后续只能靠git reflog碰运气找回。所以慎用-D,先确认分支内容真的不需要了。
推送分支到远程:
bash复制git push -u origin feature/login-page
-u参数的作用是建立本地分支与远程分支的跟踪关系。只要推送过这一次,以后在这个分支上直接git push和git pull就能自动对应到远程的同名分支,不用再手动指明。这个参数在新分支第一次推送时一定要带,省掉的麻烦远超想象。
3.3 分支合并的两种方式:merge与rebase
分支开发完,终归要把代码并回主线。Git提供两种主要方式:git merge和git rebase。这两种方式在网上吵了很多年,我没有兴趣参与站队,只说清楚它们的行为差异和适用场景。
**merge(合并)**的逻辑是把两个分支的历史汇合到一个点,产生一个额外的合并提交。它的特点是:历史记录是分叉再汇聚的形态,完整保留了每个分支的真实开发过程。优点是操作安全、容易被理解,缺点是在分支开发时间很长的情况下,历史图会变得错综复杂。
bash复制git checkout main
git merge feature/login-page
执行merge时如果没有任何冲突,Git会自动创建一个合并提交;如果两个分支改了同一处代码,就会进入冲突解决流程。
**rebase(变基)**的逻辑则是把当前分支上的提交“摘下来”,重新接到目标分支的最新提交之后。它的特点是:历史记录变成一条直线,非常整洁。优点是历史干净、线性,看起来舒服;缺点是它重写了提交的基线,如果分支已经推送到远程且有人基于它开发,rebase会带来麻烦。
bash复制git checkout feature/login-page
git rebase main
执行完rebase之后,feature分支的实现还是那些实现,但它现在看起来就像从main的最新位置直接长出来的。之后需要强制推送才能更新远程分支:
bash复制git push --force-with-lease
这里特别指出--force-with-lease和--force的区别。--force是无条件覆盖远程分支,如果此时远程分支有别人新推送的提交,你的强制推送会直接把它们丢掉;--force-with-lease则会在推送前检查远程分支是否和你上次拉取时的状态一致,不一致就拒绝推送。日常一定要用带lease的版本,这是保护自己也是保护队友的最后一道防线。
从实操角度给个建议:如果分支只有你自己在用,rebase和merge看你个人偏好;如果分支被多人共享,优先用merge,避免重写他人已经拉取过的提交历史。
3.4 合并冲突的解决流程
冲突是每个用Git的人迟早都要面对的。它的本质不是Git本身出了问题,而是两个分支在相同位置做了不同的修改,Git没有能力自动判断谁对谁错,只能请人来裁决。
模拟一个典型冲突场景:main分支里有一个config.js文件,某行为port = 8080,你在feature分支上把它改成了port = 3000,你的同事在main分支上把这行改成了port = 4000。等到合并时,Git发现两边的改动都作用于同一行,无法自动决定,于是报告冲突。
要看的命令是:
bash复制git status
处于冲突状态时,git status会明确列出both modified: config.js这种提示。打开这个文件,你会看到类似下面的冲突标记:
text复制<<<<<<< HEAD
port = 4000
=======
port = 3000
>>>>>>> feature/login-page
=======上方的内容代表当前分支(HEAD)的版本,下方代表正在合并进来的分支的版本。你需要做的事情是:决定保留哪个、还是两者都保留,然后删除冲突标记,把这个文件改成最终想要的形态。手动改完之后执行:
bash复制git add config.js
git commit
注意,合并冲突解决后的提交不需要加-m参数,Git已经为你准备好了合并提交信息模板。如果你漏掉了某个冲突文件的处理,git commit会被拒绝,提示还有未解决的文件。
冲突解决的根本思路不是“消除冲突”,而是“正确决策”。有些冲突涉及双方改动不同功能但同一区域代码,需要和队友沟通后决定怎么合。头几次遇到冲突慌是正常的,多经历几次就会发现:解冲突实际上是代码审查的一种特殊形式。
4. 远程协作与SSH认证问题排查
“ssh认证失败 git”这组关键词在搜索榜上长时间不下榜,不是没有原因的。SSH配置虽然只花一次时间,但中间任何一环出错,报错信息还异常抽象,排查起来非常劝退。这一节就把SSH密钥的完整配置流程和常见失败原因一次性讲透。
4.1 SSH密钥生成与配置详解
SSH认证的原理先花30秒理解一下,后面遇到问题排查起来更有方向。SSH密钥是一对非对称加密密钥,分为私钥和公钥。私钥保存在你自己电脑上,相当于你的身份证明,绝对不能泄露给任何人。公钥则是可以公开的,需要添加到你的代码托管平台账号里。当你的电脑和远程服务器通信时,服务器用你提供的公钥验证你的身份,确认你就是私钥的持有者,从而完成认证。整个过程不需要输入密码。
生成密钥的命令:
bash复制ssh-keygen -t ed25519 -C "your_email@example.com"
用ed25519算法是当前的主流选择,它比传统的RSA算法更安全、密钥更短、生成速度更快。如果你的系统上Git版本比较老,或者需要连接一些对ed25519支持不佳的旧系统,也可以用ssh-keygen -t rsa -b 4096,但新项目基本都用ed25519。
执行这条命令后,终端会问你三个问题,大多数人就在这里开始迷糊了:
- 第一个问题
Enter file in which to save the key:密钥保存路径。直接按回车使用默认路径~/.ssh/id_ed25519即可,不需要改。 - 第二个问题
Enter passphrase:密钥口令。输入后,以后每次使用私钥都要输一遍口令。如果你是在自己个人电脑上,可以留空直接回车;如果是在公司电脑或公共设备上,建议设置一个,防止私钥泄露后被人直接使用。 - 第三个问题
Enter same passphrase again:重复确认口令。
生成完成后,用下面这条命令查看公钥内容:
bash复制cat ~/.ssh/id_ed25519.pub
把输出的完整字符串复制下来,登录到你的代码托管平台,找到Settings里的SSH Keys页面,粘贴保存即可。到这一步,本地私钥和平台公钥就完成了配对。
4.2 ssh认证失败git的典型排查
SSH认证的报错通常长这样:
text复制git@github.com: Permission denied (publickey).
看到Permission denied (publickey),基本可以断定服务器根本没有收到你的有效公钥。常见原因按出现频率排序如下:
原因一:公钥没添加到托管平台或者添加错了账号
这个原因出现率最高,尤其当一个开发者有多个托管平台账号时,很容易把A账号的公钥加到B账号上。排查方式是执行:
bash复制ssh -T git@github.com
提示Hi username! You've successfully authenticated说明认证成功,并且会显示对应的用户名。如果你在此处看到的用户名和自己预期不符,那问题就清楚了。
原因二:SSH客户端没找到私钥文件
Git使用SSH时,默认会在~/.ssh目录下按固定名称查找私钥文件。如果你生成密钥时自定义了文件名(比如id_ed25519_custom),Git默认是找不到的。解决方案是启用SSH config配置文件:
bash复制# 编辑 ~/.ssh/config
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_custom
或者直接设置Git使用自定义私钥路径:
bash复制GIT_SSH_COMMAND="ssh -i ~/.ssh/id_ed25519_custom" git clone git@github.com:someorg/somerepo.git
原因三:SSH agent没加载私钥
这种情况在新安装Git后尤为常见。你明明有私钥,但系统没有自动加载。手动添加即可:
bash复制eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
原因四:私钥文件权限过宽
Linux和macOS系统对私钥文件权限很敏感,如果其他用户也能读取私钥,SSH会直接拒绝使用:
bash复制chmod 600 ~/.ssh/id_ed25519
chmod 700 ~/.ssh
原因五:首次连接时的host key确认问题
有时候你clone时看到的是这样的错误:
text复制The authenticity of host 'github.com (IP)' can't be established.
这往往发生在第一次连接、或者服务器公钥发生过变化时。正常确认后输入yes即可。但如果你从来没连接过却收到这类警告,留意一下网络环境中是否有中间环节拦截了SSH流量。
4.3 远程仓库同步的常用命令
SSH搞定之后,日常远程操作主要集中在这几个命令上。
拉取远端更新:
bash复制git pull
要注意,git pull实际上是git fetch和git merge两条命令的组合。git fetch的作用是只把远程的最新提交记录拉到本地,但不会动你当前工作区的任何文件;git merge才把远程分支合并到当前分支。所以如果只想“看一眼远端有什么新东西,暂不合并”,用:
bash复制git fetch
git log origin/main --oneline
推送本地提交:
bash复制git push
需要说明的是,如果你的本地分支没有设置远程跟踪分支,直接git push会报错提示找不到上游分支。此时按提示执行git push -u origin 分支名即可。
删除远程分支:
bash复制git push origin --delete feature/old-page
这条命令会同时删除远程仓库的分支记录。注意,这可能会影响其他协作者已经checkout到本地的分支,操作前先确认团队共识。
查看远程地址:
bash复制git remote -v
如果发现克隆地址不对,需要修改远程仓库地址,可以这样操作:
bash复制git remote set-url origin git@github.com:someorg/somerepo.git
我在实际工作里遇到过几次这种场景:公司项目从内网GitLab迁移到了新的服务器,所有人拉代码时提示找不到主机而不知所措。一条git remote set-url就能解决,知道的人10秒搞定,不知道的人还在琢磨重新克隆然后搬移本地未提交代码。
5. 常见问题与避坑实录
这一节把日常使用Git时最高频的几个问题整理成表格,每个都附上最简单直接的解决路径。这些解法不是我抄文档抄来的,全是实际开发中踩过之后验证过的。
5.1 高频问题速查表
| 问题现象 | 根本原因 | 最快解决方案 |
|---|---|---|
| git commit报错Please tell me who you are | 未配置user.name和user.email | git config --global user.name "名字" + git config --global user.email "邮箱" |
| 提交时看到一堆文件被标记为modified但内容没变 | 换行符差异 | 统一团队.gitattributes配置,或调整core.autocrlf设置 |
| git pull提示“Your local changes would be overwritten” | 本地有未暂存改动与远端冲突 | 先git stash保存现场,拉取后git stash pop恢复 |
| 误提交了大文件或敏感信息 | 没提前配置.gitignore或疏忽 | git rm --cached移除跟踪;敏感信息还需要在托管平台历史中彻底清理 |
| 强制推送后队友的提交不见了 | 使用了git push --force覆盖远程分支 |
让队友用git reflog找回本地原提交;切记以后改用--force-with-lease |
| 合并冲突后想放弃当前合并 | 合并过程中发现问题 | git merge --abort恢复正常状态 |
这里有两个值得展开说说的日常高频操作。
git stash的应用场景非常广:你正在功能分支上写着代码,突然需要切回main分支看一个问题,但手头的改动还没到可以提交的程度。如果直接切换分支,Git会拒绝切换并提示未提交的改动会丢失。这时候用:
bash复制git stash # 把当前改动临时保存起来
git checkout main # 放心切换分支
# 处理完之后回到功能分支
git checkout feature/login-page
git stash pop # 恢复之前保存的改动
git stash pop是恢复并删除最近的stash记录。如果你想保留多个stash,可以用git stash list查看所有保存记录,git stash apply stash@{0}恢复指定记录但不删除。
git log的进阶用法属于看着简单但实际效率提升巨大的部分。全量提交历史经常刷屏,这时可以使用一行模式:
bash复制git log --oneline --graph --all
--graph参数让提交历史以ASCII图的形式展示分支分叉和合并关系,配合--all查看全部分支的脉络,基本可以替代图形化工具完成大部分历史审查工作。
5.2 我踩过的坑:几个真实案例
挑选三个我真正遇到过的、且网上文档里不易直接找到答案的坑,分享完整的排查过程。
第一个坑:误以为.gitignore会立即生效
很久之前,我把一个包含大量日志文件的目录加入了.gitignore,但git status依然显示这些文件是modified状态。检查.gitignore写法没错,路径也对,想不通哪里出了问题。后来才意识到:这些文件早在加入.gitignore之前就已经被Git跟踪了,所以忽略规则根本没有覆盖它们。这类问题的最快确认方式就是执行git ls-files看哪些文件被跟踪:
bash复制git ls-files | grep "logs/"
如果有输出,说明这些文件还在Git的索引里。处理方案就是我前面讲过的git rm -r --cached,从索引移除跟踪关系,之后.gitignore规则才会真正接管。
第二个坑:clone下来的仓库密码错误导致反复认证
曾经有一个项目,同事换了密码后让我帮忙排查“为什么每次pull都要求输入密码,输了还是报错”。我检查了远程地址,发现用的是HTTPS,于是想到Windows的凭证管理器里存了旧密码。Git每次请求认证时都会优先取用存储的旧凭据导致失败。排查方式是控制面板里找到凭据管理器,删除对应条目的旧凭据,或者直接在Git中切换到SSH方式重新认证。这类问题只出现在HTTPS连接方式,也是我后来在所有长期项目上坚持用SSH的原因之一。
第三个坑:rebase之后发现分支历史“丢”了
有次我用git rebase整理一个功能分支的提交历史,操作过程中发现rebase结果里少了一个提交。当时心里一凉,以为那个提交彻底消失了。后来冷静下来想到git reflog可以查看所有HEAD移动的痕迹,一查发现rebase之前的HEAD位置还记在那里,顺着reflog定位到旧提交的哈希,用git cherry-pick把它重新挑选回来,问题解决。从那之后我形成了一个习惯:做任何涉及历史重写的操作之前,先记录当前HEAD位置,或者直接创建一个备份分支。哪怕操作失误,也有回头路可走。
写在最后的Git使用基本功
根据我个人的使用经验,Git学到一定程度之后,真正拉开高手的差距已经不再是背更多命令,而是对状态模型的理解是否深刻、对可逆操作的边界是否清楚。知道哪些操作是安全的(比如commit、merge、fetch),哪些操作具有破坏性(比如reset --hard、push --force、rebase),以及如何给破坏性操作留好后路,这比记住任何一条命令的都重要。
如果你才开始接触Git,我的建议很简单:把本文里的命令逐个在真实项目里敲一遍,不要只读不练。遇到不理解的地方,就想想工作区、暂存区、版本库这三个区域的状态流转,大部分困惑都会解开。命令忘了随时查,概念通了才是真的会。
