1. 为什么要整理一份Git自用手册
Git这个东西,我前后大概用了五六年,从最初只会git add、git commit、git push三板斧,到现在日常处理分支、解决冲突、恢复误删提交,已经变成了肌肉记忆。但有一件事一直没变:总有些命令和参数,隔几个月不用就忘得一干二净。每次都要翻官方文档或者重新搜一遍,效率很低。这份手册就是我在这个背景下整理出来的,专门记录那些高频使用、但又容易记混的Git操作。
与其说这是一份教程,不如说是一份"自用手册"——里面没有太多教科书式的理论,大部分都是从实际项目里踩坑踩出来的经验。Git说到底不是一个需要背完所有命令才能上手的工具,你只需要掌握20%的操作就能覆盖日常80%的工作。这份手册的核心目标,就是把这些"20%的关键操作"整理清楚,同时把容易踩坑的细节指出来。
2. 环境准备:Git安装与初始配置
2.1 不同平台的Git安装方式
不管你是Windows、macOS还是Linux用户,安装Git本身都不复杂,但不同平台有几个细节值得注意。
Windows用户最简单的办法是去Git官网下载安装包,一路默认安装就行。官网下载速度如果不太理想,也可以考虑国内镜像站。安装时有一个选项会问是否把Git加入系统PATH,这个一定要勾选,否则后续在命令行里敲git命令会提示找不到。另外建议在安装时选择"Checkout as-is, commit as-is"的换行符处理方式,或者直接使用默认选项问题也不大,这个后面单独讲。
macOS用户有几种选择:如果你装了Homebrew,一条brew install git就搞定;也可以安装Xcode Command Line Tools,系统里会有自带的Git版本。不过需要注意的是,```bash git --version
输出结果里有git版本号就算成功了。我见过不少人在这一步就卡住,大概率是PATH没有配好,或者终端没有重启。装完后新开一个终端窗口再试,基本能解决。 ### 2.2 安装后的三件套配置 Git装完之后,第一件事不是急着建仓库,而是先配置身份信息。很多人跳过这一步,结果提交代码时Git用了一个系统自动生成的默认身份,比如 `user@hostname`,后面协作时别人根本不知道这个提交是谁做的。更麻烦的是,后续想改历史提交的作者信息,操作起来非常繁琐。 需要配置的第一个是用户名,第二个是邮箱,命令如下: ```bash git config --global user.name "你的名字" git config --global user.email "你的邮箱"这里的--global表示全局配置,也就是说这台机器上所有仓库默认都会用这个身份。如果你在某个特定项目里想用另一个身份,可以在项目目录下不带--global再执行一次,那个仓库的配置会覆盖全局配置。这个机制很适合同时有个人项目和公司项目的人。
还有一个值得提的配置是命令行颜色。默认情况下Git的输出就是纯文本,看着特别费劲。建议开启颜色显示:
git config --global color.ui true再执行git status和git diff的时候,输出会带上红绿颜色,哪些文件改了、哪些地方删了增了一目了然。这个配置虽然不起眼,但对日常使用的体验提升非常明显。
2.3 配置检查与多身份管理
配置完之后,可以用git config --list查看当前所有配置。这个命令会同时列出系统级、全局级和仓库级的配置,如果同一个配置在多个级别都有,后面的值会覆盖前面的。
提示:如果输出结果太长,可以直接
git config --list | grep user来单独查看用户信息。
多身份管理是我实际工作中经常遇到的问题。比如我给公司项目提交时要用公司邮箱,给开源项目提交又希望用个人邮箱。这种情况下,不要在全局配置里只写一个身份,而应该把全局身份设置为最常用的那个,然后在特定仓库里用仓库级配置覆盖。具体操作就是在项目根目录下执行:
git config user.name "公司用户名" git config user.email "公司邮箱"不带--global的配置会写进当前仓库的.git/config文件里。这个文件只对当前仓库生效,不会影响其他项目。
另外,如果你同时使用多个代码托管平台,比如一个是内部的GitLab,一个是GitHub,建议直接用SSH配置来处理。因为SSH key可以在不同平台上绑定不同的邮箱和用户名,后面会专门讲免密配置部分。
3. 高频命令实操:覆盖日常80%的操作
3.1 先理解Git的三个区域
很多人学Git卡住的第一个点,就是搞不清楚add、commit这些命令到底在操作什么。这里用一个生活化的类比来解释。
Git仓库可以分成三个区域:工作区、暂存区、版本库。工作区就是你电脑里实际能看到和编辑的文件目录;版本库是Git用来保存提交记录的地方,在项目目录下的.git文件夹里;暂存区是工作区和版本库中间的一层缓冲区,你可以把它理解成去超市购物时的购物车。购物时你会把商品先从货架上拿下来放到购物车里,这个动作就相当于git add,然后统一去收银台结账,相当于git commit。如果只是往购物车里放了东西但没结账,那超市(版本库)里还没有这条记录。
这个认知非常重要,因为后面所有命令都是围绕这三个区域展开的。比如git status看的就是三个区域之间的差异状态,git diff默认看的是工作区和暂存区之间的改动,加上--cached参数看的就是暂存区和版本库之间的差异。
3.2 最常用的状态与提交命令
日常开发的典型流程是:改代码 -> 查看改动 -> 暂存 -> 提交 -> 推送。下面这几条命令就是整个流程的骨干。
首先是git status,这个命令在Git所有命令里使用频率几乎是最高的。它会清楚地告诉你当前分支、是否有未跟踪的文件、哪些文件已修改但未暂存、哪些已暂存但未提交。如果不确定下一步该执行什么操作,先敲git status基本不会有错。
然后是查看具体改了什么,用git diff。这个命令按文件对比工作区和暂存区的内容差异。我在提交之前的习惯是必跑一次git diff,目的是防止把调试用的临时代码或者不小心改错的地方提交上去。特别是有些编辑器会自动替换行尾或者格式化代码,导致diff里出现大量和逻辑无关的改动,提前检查能避免很多尴尬。
接下来是暂存和提交:
git add <文件名> # 暂存指定文件 git add . # 暂存当前目录下所有改动 git commit -m "提交说明"这里要强调一个习惯:尽量别用git add .一把梭。如果项目里混入了生成文件、临时文件,或者你根本不清楚哪些文件被改动了,git add .会把不该提交的内容也放进来。更稳妥的方式是先git status看清楚,再针对性地git add具体文件。如果确实有多次改动、希望分门别类地提交,可以先git add一部分文件,提交一次,再git add另一部分,再提交一次。这样每次提交的粒度小、主题明确,后面如果需要回滚或者排查问题会轻松很多。
git commit的提交说明也值得认真写。我自己采用一个简单的规范:第一行用一句话说清楚这次提交做了什么,动词开头,比如"修复登录接口空指针异常"、"优化列表页首次加载性能"。这样在git log里扫一眼就能明白每次提交的意图,比写"修改bug"、"更新代码"这种没有任何信息量的说明强太多。
3.3 后悔药:撤销与回滚
Git之所以让人安心,是因为几乎所有误操作都有补救办法。这一节把最常用的撤销和回滚命令整理成一张速查表。
| 场景 | 命令 | 说明 |
|---|---|---|
| 工作区改了但没暂存,想放弃修改 | git checkout -- <文件> | 会丢失工作区的改动,不可逆,慎用 |
| 已暂存但没提交,想取消暂存 | git restore --staged <文件> | 从暂存区退回工作区,文件内容不变 |
| 已提交但没推送,想撤掉这次提交 | git reset --soft HEAD~1 | 撤销commit,保留改动到暂存区 |
| 已提交但没推送,想彻底撤销 | git reset --hard HEAD~1 | 丢弃提交及所有改动,非常危险 |
| 已推送,想撤销但保留历史 | git revert <提交ID> | 生成一个反向提交,安全可推送 |
git reset有三个模式,很多人搞混。--soft只撤销commit,把改动放回暂存区;--mixed是默认模式,撤销commit和暂存,把改动放回工作区;--hard直接丢弃所有改动。我个人的建议是,除非你非常确认那些改动已经不需要了,否则不要用--hard。宁可多用一步,先把当前状态备份到另一个分支,再执行重置操作。
git revert和git reset的区别值得单独讲一下。reset是"回到过去",它会移动分支指针,历史记录里那次提交就消失了;revert是"否定过去",它不会删除原提交,而是新生成一个反方向的提交来抵消之前的改动。如果是已经推送到远端的提交,请一定用revert,不要用reset。因为reset会改变提交历史,下一次push时远端会拒绝非快进推送,除非你强制推送,而强制推送在协作分支上可能把队友的历史搞乱。
3.4 让日志更清晰的几个技巧
git log是所有命令里输出信息最丰富的,但默认格式也比较冗长。我常用的几个参数组合如下:
git log --oneline # 每个提交只显示一行,最常用 git log --graph # 显示分支合并图,直观看到分之走向 git log --oneline --graph --all # 查看所有分支的提交历史 git log --author="名字" # 只看某个人的提交 git log -p # 查看每次提交的具体diff内容我个人最常用的是git log --oneline --graph --all,这个组合能在一个页面里看到整个项目的分支走向、合并节点和各个分支的提交位置。在排查"某个功能是在哪次合并进来的"这类问题时特别好用。
另外提一个实用的小命令:git log --oneline -10只看最近10条提交。如果项目提交频率很高,这个命令能避免日志刷屏,快速定位近期改动。
4. 分支管理:并行开发的基石
4.1 分支的本质
Git的分支模型是它区别于SVN等旧版本管理工具的核心优势。Git的分支本质上只是一个指向某个提交的指针,创建分支的成本极低,所以Git鼓励多建分支、多用分支。
实际开发中,我习惯的做法是主分支保持稳定可用状态,所有新功能都从主分支拉出独立的分支来开发,功能完成后再合并回去。这样做的最大好处是隔离风险:某个功能写了一半、甚至写坏了,都不影响主分支上其他人的工作。
创建和切换分支的命令:
git branch feature-login # 创建分支 git checkout feature-login # 切换到分支 # 或者用新版命令一步到位 git switch -c feature-login # 创建并切换git switch是Git 2.23之后引入的替代命令,语义更明确:git switch只管切换,git branch只管增删改查。用checkout也能做同样的事,但它的职责太多,新手容易混淆。我个人的建议是能记住git switch就多用它,而git checkout -- <文件>这种撤销用法保留在撤销场景里。
4.2 合并与冲突
功能开发完,需要把分支合并回主分支。标准操作是先切回主分支,然后执行合并:
git checkout main git merge feature-login如果两个分支改动的文件互不重叠,Git会自动完成合并,生成一个新的合并提交。如果改动了同一个文件的同一块区域,Git就会报冲突,并在冲突文件里插入类似下面的标记:
<<<<<<< HEAD 这是当前分支(main)的代码 ======= 这是要合入分支(feature-login)的代码 >>>>>>> feature-login解决冲突的思路不是直接在编辑器里删掉标记,而是仔细看两边的代码,决定保留哪一边、或者两边都保留并做调整。改完之后执行git add <文件>,再执行git commit完成合并。这里不要用git merge --abort一遇到冲突就想退出,除非你确定这次合并不应该继续。大多数情况下,冲突是正常的,认真解决就好。
如何减少冲突?我在团队里推广过几个经验:第一,主分支和功能分支都尽量小步提交,不要攒几百个改动一次性合并;第二,功能分支开发过程中,定期把主分支的最新代码合并进来,这样最后合并回去时冲突范围会小很多;第三,不同成员尽量少改同一文件的同一区域,这属于任务拆分的范畴,但确实是减少冲突的最有效手段。
4.3 merge还是rebase:这是永恒的争论
关于git merge和git rebase的讨论,几乎是每个Git使用者绕不开的话题。简单理解:merge会把两个分支的历史合并成一个新的节点,保留分叉记录;rebase会把当前分支的提交"重新放"到目标分支的最新提交之后,让提交历史变成一条直线。
| 对比维度 | merge | rebase |
|---|---|---|
| 历史形态 | 有分叉,有合并节点 | 线性,没有合并节点 |
| 可读性 | 能看出开发并行情况 | 更简洁,易于追踪 |
| 安全性 | 不改变已有历史 | 会重写提交,需要谨慎 |
| 适用场景 | 公共分支整合、保真历史 | 整理本地提交、保持主线干净 |
我的个人建议是:在自己的功能分支上,可以使用rebase来保持分支整洁;但永远不要在公共分支上执行rebase,因为rebase会重写提交哈希,导致所有基于旧提交的协作者都出现历史不一致的问题。
git rebase -i是交互式rebase,可以让你压缩提交、修改提交信息、调整提交顺序。我经常在推送之前用它把开发过程中的"临时提交"、"调试提交"合并成一个完整的功能提交。操作方式是在当前分支上执行:
git rebase -i HEAD~3这会打开一个编辑界面,列出最近3条提交。每个提交前面是pick,你可以把它改成squash(压缩到上一个提交)、reword(修改提交信息)等。保存退出后,Git会按照新的配置重组提交。这个命令很强大,但我建议只在自己本地的、未推送的分支上使用,推送到远端之后就不建议再动了。
5. 远程仓库协作与免密配置
5.1 remote相关命令
本地仓库要跟远程仓库对接,首先得添加远程地址。一般克隆一个仓库之后,远程地址会自动配置好,名字叫origin。如果你是从零搭建的项目,需要手动关联:
git remote add origin <远程仓库地址> git remote -v # 查看当前配置了哪些远程地址日常协作最常用的两个命令是push和pull。push把本地提交推送到远程,pull把远程更新拉取到本地。git pull实际上等于git fetch加git merge——先获取远程的更新,再合并到当前分支。
这里有一个很多人初期会犯的错:直接在main分支上开发,然后git push时发现被拒绝,提示"远端有你在本地没有的提交"。这时千万不要用git push --force强行覆盖,正确做法是先git pull把远端更新拉下来,解决冲突后再推送。
给push设置上游分支也是一个常用操作。首次把本地分支推送到远程:
git push -u origin feature-login-u的作用是设置上游追踪关系。设置之后,后续在这个分支上直接敲git push和git pull就行了,不用再指定远程和分支名。
5.2 SSH还是HTTPS:免密配置的两种思路
免密配置是Git使用体验的一大飞跃。我见过太多人在每次push/pull时反复输入账号密码,非常影响效率。这里提供两条路:SSH方式 和 HTTPS凭据管理方式。
SSH方式是大多数开发者的首选。原理是生成一对公钥和私钥,把公钥放到代码托管平台上(GitHub、GitLab等都支持),私钥留在本地。之后Git通过SSH协议连接远程服务器时,服务器确认公钥匹配,就直接放行,不需要再输密码。
生成SSH key的命令:
ssh-keygen -t ed25519 -C "你的邮箱"一路回车就行,会在~/.ssh/目录下生成id_ed25519.pub公钥文件和id_ed25519私钥文件。然后把公钥内容复制出来:
cat ~/.ssh/id_ed25519.pub在GitHub的Settings -> SSH and GPG keys里新增一个Key,把内容粘贴保存。之后本地仓库的远程地址使用SSH格式,比如git@github.com:用户名/仓库名.git,push和pull就完全免密了。
用SSH还有一个额外的好处:如果你的SSH key设置了密码(passphrase),可以使用ssh-agent来做内存级缓存,这样每次会话只需输入一次密码。macOS上还可以让系统钥匙串帮你管理。
如果不想折腾SSH,HTTPS方式也有对应的免密方案。Git内置了credential helper,Windows上安装Git时会自动配置,第一次输入用户名密码后凭证会被保存到Windows凭据管理器里,之后自动读取。macOS同理,会存到钥匙串里。还可以手动开启:
git config --global credential.helper store这样凭证会明文存在~/.git-credentials文件里。不过这种方式的便捷性是以安全为代价的,文件泄露就等于账号泄露。我建议优先使用SSH,或者使用系统级的credential helper,而不是明文store。
另一个常见场景是使用Personal Access Token。现在GitHub要求输入密码的位置必须用token代替密码,所以如果你用HTTPS方式连接GitHub,需要先去Settings里生成一个token,然后把它当密码输入。token本身要妥善保存,生成后只显示一次。
5.3 远程协作中的典型问题
远程协作中最常见的报错是! [rejected] main -> main (non-fast-forward)。通俗解释就是:你本地的提交历史和远程不一致,Git担心你推送会覆盖掉其他人的提交,所以拒绝执行。
遇到这个情况,我的排查顺序是:
- 先
git status看当前状态,确认没有未提交的改动。 - 执行
git fetch获取远程最新状态。 - 对比本地和远程的分叉情况:
git log --oneline --graph --all。 - 执行
git pull拉取并合并远程更新,解决冲突后再push。
在多人协作场景下,我强烈建议定一个规矩:不要在main分支上直接提交和推送。每个人都在自己的feature分支上开发,通过合并请求(Pull Request / Merge Request)合入主干。这样既能做代码审查,又能避免直接推送到主干带来的混乱。
团队项目还有一个容易被忽略的点:大文件。Git原生不适合存放大文件,仓库里有几个上百MB的文件,clone速度会变得很慢。如果项目确实需要放大的二进制文件,可以考虑Git LFS(Large File Storage)方案,但这是另一个话题了,这里只提醒一句:小心大文件进库,一旦进了历史,想清理干净非常麻烦。
6. 常见问题与排查技巧实录
6.1 高频问题速查表
下面这张表是我这些年实际遇到过的、也帮别人排查过的问题汇总,按频率排序整理:
| 问题现象 | 原因 | 解决办法 |
|---|---|---|
git status显示中文文件名乱码 | Windows下编码问题 | 执行git config --global core.quotepath false |
git commit报Please tell me who you are | 未配置用户名邮箱 | 按第2.2节配置 user.name/user.email |
推送时提示non-fast-forward | 本地历史落后于远端 | git pull合并后再push,不要用强推 |
| 本来只想提交一个文件,结果一堆文件进来了 | git add .一次暂存了全部 | 用git add <具体文件>或git restore --staged <文件>取消暂存 |
| 提交信息写错了还没push | 提交还停留在本地 | 用git commit --amend -m "新的提交信息"修改 |
| 切分支后找不到自己写的代码 | 可能提交在了另一个分支 | git log --oneline --all --grep=关键词全局搜索提交 |
| 误删了一个分支,后悔了 | 分支被删除 | 用git reflog找到删除前的提交哈希,重建分支 |
git clone速度很慢 | 网络或仓库过大 | 检查网络;仓库内是否有大文件,必要时用浅克隆--depth=1 |
| 终端里输入Git密码一直被拒绝 | 平台要求使用token | 去平台Settings生成token,代替密码输入 |
git reflog是很容易被忽略但极其实用的命令。它记录了HEAD指针的所有历史移动,哪怕你执行了git reset --hard或者删除了分支,只要reflog里还能找到之前的提交哈希,就能恢复。有一次我在一个分支上误操作,用git reset --hard丢弃了两个小时的代码改动,当时心都凉了。后来用git reflog找到丢失提交的哈希,git branch 恢复分支名 提交哈希,两分钟的功夫全部找回。这个命令建议每个Git使用者都记住。
6.2 保命经验分享
最后几条经验,是我在多次项目踩坑后总结的,频率不高但每次都价值巨大。
第一,提交之前先看diff。这个习惯可以帮你挡住90%的误提交。代码写完之后,先git diff检查一下改了什么,确认没有调试语句、没有临时文件、没有遗漏的改动,再走add和commit。
第二,做危险操作前先备份分支。不管是reset、rebase还是force push,动手之前先执行:
git branch backup-当前日期这条命令创建一个当前状态的分支备份,万一操作搞砸了,随时能切回去。成本极低,收益极大。我现在的习惯是每周至少一次给主要分支打个备份标签,或者在做任何历史整理操作前必做备份。
第三,提交信息要让人看得懂。这个前面提过,但值得再强调一次。好的提交信息能在半年后帮你快速定位某次改动的意图,而不好的提交信息("modify"、"update"、"fix")等于没有。可以在项目里约定一个简单的模板:类型+简述,比如fix: 修复空指针、feat: 新增导出功能。
第四,不要轻易强制推送。git push --force是一把双刃剑,它会让远端分支的历史直接变成你本地的样子,队友的提交可能因此丢失。如果确实必须强推,先通知团队,确保每个人都拉取了最新内容。现在Git提供了--force-with-lease这个更安全的参数,它会检查远端是否发生了你本地不知道的更新,如果有就拒绝推送,没有才允许。我建议把强推的默认选项改成--force-with-lease。
第五,养成看报错全文的习惯。很多人看到一个英文报错就直接复制到搜索框,这没有错,但更快的办法是读懂报错。Git的报错信息一般是"问题描述+解决方案提示",比如Please tell me who you are后面就跟着配置user.name和user.email的提示命令。先认真读一遍报错,再决定怎么处理,这个习惯能让你的排查速度快上一倍。
7. 一点个人体会
Git这个东西,一开始会觉得命令多、概念抽象,但只要把工作区、暂存区、版本库这个框架搭起来,再把提交、分支、合并、远程协作这几条主线走一遍,框架就清晰了。剩下的命令基本都是按需查、按需记,用多了自然就熟了。
我现在每换一台新电脑,第一件事就是装Git、配好用户名邮箱、生成SSH key并绑定到GitHub。这个过程走了很多遍,心得就是:一次性把这些配置做好,后面能省掉无数麻烦。希望这份手册也能帮你把Git的使用体验理顺,少踩一些我踩过的坑。