直接开始。这篇是《Git入门指南》系列的第二篇,上一篇咱们把安装、配置、SSH 这些地基打好了,这一篇就进入正题:日常用 Git 干活最频繁的那批基本操作。从 git init 到 commit,从 diff 到 log,从分支到标签,再到远程仓库的协作,我把这一年多在项目里真正摸过的命令、踩过的坑、总结出的习惯,一次讲清楚。
如果你刚接触 Git,只想知道“每天要怎么用它”,那这篇正好合适。如果你已经会 clone、commit,但一直搞不懂工作区、暂存区到底怎么回事,或者老是卡在合并冲突上,这篇也能帮你把逻辑理顺。
1. 环境准备:安装 Git 前先想清楚这三点
我知道很多人是直接一把梭把 Git 装上,但用了一两个月之后回过头来发现:版本选错、换行符没配、终端用得不顺手,各种小毛病全冒出来了。所以这里先把环境准备说透。
1.1 版本选择:为什么没必要追新
Git 官方版本更新节奏不快,但也不慢。很多人一看有新版本就冲上去升级,结果公司内网服务器比较旧,或者团队成员用的版本太低,互相之间交换仓库没出问题还好,一旦出了兼容性提示就很烦。
我的建议是选一个稳定版本,比如当前主流的 2.30 到 2.40 区间,不要追求最新,更不要用老古董版本。具体到 Windows,直接去 Git 官网下载安装包;macOS 上如果装了 Homebrew,用 brew install git 就完事;Linux 就看你发行版对应的包管理器。
这里有个细节值得提一下,Windows 用户安装时一定会遇到一个选项:调整 PATH 环境变量。默认选项是Git from the command line and also from 3rd-party software,这个就用默认的,不要改成 "Use Git and optional Unix tools from the Command Prompt",否则会把 Windows 自带的 sort、find 这些命令覆盖掉,后面在脚本里容易出问题。
1.2 换行符配置:最容易被忽略的坑
Git 官方在这点上做了妥协:Windows 用 CRLF(回车+换行),Linux 和 macOS 用 LF(换行)。跨平台协作时,如果不处理,你 pull 下来的代码可能每一行都被标成改动过,diff 看起来像整个文件都变了。
安装时那个Checkout Windows-style, commit Unix-style line endings选项,意思是在工作区转成 CRLF,提交到仓库时转成 LF。听起来很智能,但实际协作中我踩过不少坑:如果是纯 Windows 团队项目,问题不大;一旦有 Linux 或 macOS 同学加入,就可能出现“我明明没改这行,Git 却总提示冲突”的诡异现象。
所以我的做法是,团队统一在仓库根目录加一个.gitattributes文件,明确指定文本文件的换行符处理方式:
* text=auto *.js text eol=lf *.ts text eol=lf *.json text eol=lf *.md text eol=lf这个文件提交到仓库后,所有成员不管用什么系统,Git 都会按规则处理换行符。比依赖每个人的全局配置靠谱得多。
1.3 验证安装是否成功
装完别急着用,先跑一下:
git --version能看到版本号说明基本环境没问题。再跑一句:
git config --global user.name git config --global user.email这两条输出如果不为空,说明身份信息也配好了。如果为空,先补上,因为 Git 每次提交都会记录这两个字段,没配齐会让你提交时提示Please tell me who you are。很多新手第一次提交就被这个吓住,其实解决方式很简单:
git config --global user.name "your name" git config --global user.email "you@example.com"2. 初始化到第一次提交:建立版本管理的最小闭环
这一步相当于给项目装上一个“存档系统”。我先直接给出一套最常用的操作路径,然后在下面仔细拆解每一条命令背后的作用和常见误区。
2.1 两种开始方式:init 和 clone
如果你是从零开始一个新项目,就在项目根目录执行:
git init执行完会生成一个隐藏的.git目录,整个项目的版本信息都存在这里面。注意,这个目录说白了就是 Git 的“记忆库”,它里面的东西不适合直接手改,哪怕是误删一个文件,都可能让整个历史记录坏掉。
如果是从远程仓库开始协作,那就不是 init 了,而是 clone:
git clone https://github.com/user/repo.git或者用 SSH 方式:
git clone git@github.com:user/repo.git我建议有条件用 SSH 就用 SSH。原因很简单:HTTPS 方式每次 push 都要输账号密码,虽然可以配置缓存,但总归多一步操作。SSH 配置好密钥之后,push/pull 都不需要再输密码,效率高很多。
2.2 第一次提交的标准五连
初始化仓库之后,第一次提交建议按这个顺序来:
git status git add . git commit -m "feat: 项目初始化" git log很多人一上来就 add 然后 commit,中间跳过了 status 和 log,这其实不是不行,但对新手来说很容易出现“稀里糊涂提交了一堆不该提交的文件”的问题。
git status是看一眼当前仓库的状态:哪些文件是新增的、哪些被修改过、哪些已经在暂存区。git add .是把当前目录下的所有改动加入暂存区。git commit才是真正把暂存区的内容固化成一次版本记录。git log用来查看提交历史,确认这次提交是否成功。
我在第一次提交这件事上吃过的亏是:没有先写好.gitignore就把依赖目录和编译产物全提交上去了。比如 Node 项目的node_modules,Java 项目的target,Python 项目的__pycache__。这些目录动辄上千个文件,一旦进去版本库,不只是仓库体积爆增,后面每次拉代码都会觉得卡。更麻烦的是,这些文件会污染每次 diff 的视野。
所以正确的第一次提交顺序应该是:先写.gitignore,再执行上面的五连。
2.3 commit 信息怎么写才不后悔
我之前见过很多提交信息是 “update”、“fix”、“修改”,甚至还有 “123”。这种信息在写的时候特别爽,但三个月后再看,根本不知道那一次提交到底做了什么,想用git bisect定位问题都无从下手。
一个实用的格式是:类型: 简要说明。
比如:
feat: 新增用户登录接口fix: 修复移动端弹窗点击穿透问题docs: 更新 README 部署说明refactor: 重构订单状态机逻辑
原因很简单:提交信息本质上是写给未来的自己和其他协作者看的。Git 不要求信息格式,但一份好的提交信息能让团队排查问题省下一大半时间。我自己在用这种格式后,最直观的变化是回滚版本时,靠git log --oneline就能很快锁定目标提交。
3. 深入理解 Git 的暂存区:为什么不是直接 commit 文件?
这个部分我要花点篇幅专门讲,因为很多人用 Git 用了很久,仍然没弄明白暂存区存在的意义。
3.1 工作区、暂存区、版本库的关系
你可以把 Git 仓库想象成一个三层的抽屉:
- 最表层是你正在编辑的文件,也就是工作区。
- 中间一层是暂存区,类似一个“待打包区”,你想把哪些文件放进下一次提交,就先
git add把它们挪到这一层。 - 最底层是版本库,也就是历史记录。只有
git commit,暂存区里的内容才会真正形成一个不可变的版本。
为什么要多这一层暂存区?直接用“保存”不好吗?
实际开发中,一个功能可能涉及十几个文件的改动,但其中两个文件可能只是为了调试加的临时日志,不适合提交。有了暂存区,你就可以一个一个地把真正要提交的文件git add,把不该提交的排除在外。这样提交的每一个版本都是“干净”的,不会夹带调试代码。
3.2 最常用的 add 姿势
git add有好几种用法,我按使用频率排个序:
git add specific-file.js只暂存某一个文件,最精准。
git add src/暂存某个目录下的所有改动。
git add -p这个我强烈推荐,它是一个交互式命令,会逐块告诉你哪些地方有改动,让你决定是y(暂存这一块)、n(跳过)、还是s(再拆细一点)。当你在同一个文件里既有正式改动又有调试代码时,-p就是救命的。
git add .虽然简单粗暴,但前提是你对当前目录下的改动有足够的判断。我见过有人在 IDE 里无意改了一个配置文件,顺手git add .全提交了,结果导致别人的环境变量被覆盖。这种问题排查起来特别费劲,因为根本不是代码逻辑问题。
3.3 撤销后悔操作:add 错了怎么办
如果git add加错了文件,不要慌:
git reset HEAD file-name这条命令会把文件从暂存区移回工作区,但不会改动文件本身的修改内容。也就是说,你的改动还在,只是不再处于“待提交”状态。
如果连文件本身的修改也想放弃,那就用:
git restore file-name或者更传统一点的写法:
git checkout -- file-name我特别提醒一句:git restore是直接用版本库里的内容覆盖当前文件,你对这个文件做的所有未提交修改都会消失,而且不可恢复。所以执行这种命令前,最好确认自己真的不需要那部分改动了。
4. 查看变更与历史:diff 和 log 的正确打开方式
提交之前先看看自己改了什么,这是 Git 使用里最值得养成的习惯。
4.1 三种查看 diff 的层次
git diff是我每天用最多的一条命令,它分三个层次:
git diff查看工作区相对暂存区的差异。说白点,就是“我改了哪些还没 add 的”。
git diff --cached查看暂存区相对已提交版本的差异。就是“我 add 了哪些还没 commit 的”。
git diff HEAD把工作区全部未提交的改动都列出来,包括已暂存和未暂存的。
多数情况下,我习惯在 commit 之前跑一遍git diff --cached,确认即将提交的内容确实是我想提交的。这个习惯帮我避免过好多次误提交,比如把写错的接口地址、遗留的 console 日志、临时的测试配置都挡在了版本库外面。
4.2 让 log 输出更有可读性
默认的git log输出太冗长,一屏看不了几条。我平时更常用这些方式:
git log --oneline每条提交只显示一行,简洁到一眼看十条以上。
git log --graph --oneline --all以图形方式展示分支结构,适合看分支合并情况。
git log -3只看最近三条提交,适合快速确认刚才操作的结果。
还有一个特别实用的参数是--author,按提交者过滤:
git log --author="xxx"当你想复盘某个人在某个时间段里到底提交了什么,这条命令很有用。
4.3 提交信息写错了怎么办
每次git commit之后,如果发现注释写错了,或者漏了一个文件,不需要重新提交一次新 commit。可以用:
git commit --amend这条命令会把当前暂存区的内容和上一次提交合并成一条新的提交记录。换句话说,它把上一次提交“顶掉”了。
但我要强调一个使用场景限制:如果这个提交已经被 push 到了远程分支,而且有其他同事基于它拉过分支或者合过并,那就不要再 amend 了。你已经推出去的历史就这样被改写,同事那边会出现莫名其妙的冲突或者历史不一致。我在这上面吃过亏,有一次改了已经 push 的提交再强推上去,结果同事本地分支和远程分支的历史彻底错位,最后大家花了大半天才把分支恢复成一致状态。
5. 分支操作:并行开发的基石
分支是 Git 相对传统版本管理工具最核心的优势之一。它让你可以同时维护多套代码状态,互相不打扰。
5.1 分支的日常用法
查看当前分支:
git branch创建并切换一条新分支,最简写法是:
git checkout -b feature-login等价于两条命令:
git branch feature-login git checkout feature-login新版 Git 也支持用git switch来切换分支:
git switch -c feature-login我个人还是习惯checkout -b,因为这套写法兼容性最好,不管在哪台机器上都不会依赖 Git 版本。
切到新分支后,你在这个分支上的所有提交都只属于它自己,不影响主分支。等开发测试完成,再合并回主分支。
5.2 合并分支的两种方式:merge 和 rebase
合并分支时,git merge是最直接的方式:
git checkout main git merge feature-loginmerge 会把两个分支的开发历史合并产生一个新的“合并提交”,整个过程像是两条路交汇到一个点。
而git rebase则是把你当前分支的提交“摘下来”,重新接到另一个分支的最新节点后面。效果从结果上看,历史会是一条直线,看起来更干净。但代价是它改写了提交顺序,如果处理不当,同样会影响其他协作者。
我给初学者的建议是:先老老实实学会用 merge。因为 merge 的语义最简单:谁和谁合并,产生一个节点,清清楚楚。rebase 适合你已经对 Git 历史非常敏感、知道自己在干什么的情况。
5.3 合并冲突:遇到别慌
冲突就是两个分支改动了同一个文件的同一片区域,Git 不知道该听谁的。此时工作区文件里会出现这样的标志:
<<<<<<< HEAD 你当前分支的代码 ======= 正在合并进来的分支的代码 >>>>>>> feature-login你需要手工把这段内容整理成正确的代码,删掉三对有争议的标记行,然后:
git add file-name git commit注意,合并冲突并不代表代码被搞坏了,而是 Git 把裁决的权利交给了你。处理完冲突后最好重新跑一遍测试,尤其是涉及同一函数签名、同一配置项的情况,很容易出现“合并时没冲突,运行起来全是错”的隐藏问题。
6. 远程仓库协作:push、pull 和 fetch 怎么选
本地操作熟练之后,就一定要接触远程仓库了。现在大多数团队用 GitHub、GitLab 或者 Gitea 这类平台来托管仓库。
6.1 关联远程仓库
如果你的项目是从git clone开始的,那远程地址已经自动配好了。但如果你是自己git init之后想关联远程,就需要手动加:
git remote add origin git@github.com:user/repo.git这样之后的 push 和 pull 就可以默认走origin这个远程仓库了。
要查看当前关联了哪些远程地址:
git remote -v注意-v是大小写敏感的,写成git remote -V反而不会输出期望的信息。
6.2 push 的常见姿势
git push origin main把本地 main 分支推送到远程 origin。如果是第一次推送新分支,而且远程还没有对应分支,需要加上-u参数:
git push -u origin feature-login-u的含义是“设置上游分支”,设置了之后,后续在这个分支上直接执行git push就能自动找到对应远程分支,不用每次都输入 origin 和分支名。
每次 push 之前,我习惯先 pull 一下最新的远程代码,而不是等 pusl 被拒绝再补救:
git pull origin main如果你的本地分支和远程分支历史有分叉,pull 可能会自动合并,也可能直接报冲突。遇到冲突的处理方式跟分支合并时一样:解决冲突,然后提交。
6.3 pull 到底是 fetch 加 merge
很多人用 Git 很久,却不清楚 pull 和 fetch 的区别。git fetch是把远程仓库的最新提交下载到本地,但是不会动你当前分支的任何东西。git pull则是在 fetch 之后,再把远程的最新内容合并到当前分支。
所以如果你想先看看远程改了什么,不影响自己的工作,就先用 fetch:
git fetch origin git log origin/main --oneline等确认远程确实有新的提交,再决定要不要 merge 或者 pull。这个习惯在团队协作中特别好用,尤其是远程存在多条分支、你又不确定别人有没有推过改动的情况下。
我刚开始用 Git 时特别不喜欢 fetch 这个多出来的步骤,总觉得 pull 一步到位更快。但后来有一次,局部代码没提交完,pull 直接把远程的改动合并进来了,工作区的结构被弄乱,跟本地的半成品状态混在一起,处理起来相当狼狈。从那以后,我在本地还有未提交改动的时候,一定会先 fetch 看一眼,再决定怎么合。
7. 标签、储藏与忽略文件:三个高频实用点
这三个功能不算是起步就必须掌握的内容,但在真实开发中用到的频率已经高到值得专门讲一讲。
7.1 标签:给版本打上标记
打标签通常用在发版本的时候。比如代码测试通过,要发一个 v1.0.0:
git tag v1.0.0标签和分支不一样,它指向某个固定的提交,不会再移动。以后不管代码怎么迭代,都能通过这个标签快速回到这个版本。
查看所有标签:
git tag -l删除本地标签:
git tag -d v1.0.0推送标签到远程:
git push origin v1.0.0如果想一次把本地所有标签推到远程:
git push --tags标签的实战价值最大的是发布程序。我们之前上线过一个功能,部署系统就是靠 Git 标签来构建指定版本的产物。开发分支天天变,但只有带v前缀的标签才允许走发布流程,这样运维那边也能很清爽地定位到到底构建的是哪一份代码。
7.2 储藏:临时切换不再慌乱
git stash是一个被低估的命令。当你正在 feature-A 分支开发一个功能,改了一半,突然被告知需要马上切到 main 修个紧急 bug,但当前这半截改动又不想提交,怎么办?
git stash这一条命令就能把你当前未提交的改动都“储藏”起来,工作区恢复到干净状态。然后你可以放心地切走。等修完 bug,再切回来,执行:
git stash pop改动就又会恢复到原来的状态。
查看已有的储藏列表用git stash list,如果储藏了多个内容,可以用git stash pop stash@{1}指定恢复某一个。
我有一个额外建议:stash 了之后最好尽快 pop,不要在列表里积压太多。因为 stash 的内容是按日期和顺序记录的,如果你一次囤了五六个,过两周再来看,自己都想不起来每一个里面都有啥了。
7.3 .gitignore:别让无关文件污染仓库
前面提到过.gitignore的重要性,这里再系统地说一下。它就是一个纯文本文件,每一行写一条忽略规则,支持*通配符。
一个典型的 Node 项目可以这样写:
node_modules/ dist/ .env *.log .DS_Storenode_modules/表示整个目录都忽略,.env表示环境变量文件不提交,*.log表示所有日志文件都不提交。
要注意的是,.gitignore只能影响“还没被 Git 跟踪的文件”。如果某个文件已经被提交过,再往.gitignore里加它,是不生效的。这时候需要先把文件从版本库移除:
git rm --cached .env--cached参数表示只从暂存区移除,不删实际文件,这样之后这个文件就不会再被 Git 跟踪了。
8. 常见问题排查实录
这部分是我从真实工作里收集到的高频错误和信息,整理成一份速查式的列表,方便你遇到问题直接对照着处理。
8.1 “git 不是内部或外部命令”
这个报错最常出现在刚装完 Git 的 Windows 上。原因就是安装时没有把 Git 加到 PATH,或者安装后没有重启终端。
解决办法:重新运行 Git 安装包,在调整 PATH 那一步,选Git from the command line and also from 3rd-party software,装完重启命令行窗口即可。
8.2 “Please tell me who you are”
这个在 2.2 小节里提过,是因为没有配置用户名和邮箱。执行:
git config --global user.name "your name" git config --global user.email "you@example.com"一旦配好,它会影响所有仓库,所以注意用你自己的名字和常用邮箱。如果你在一个项目里要用不同身份,可以在项目目录下不加--global覆盖配置。
8.3 “does not appear to be a git repository”
你可能在一个不是 Git 仓库的目录里执行了 Git 命令。先用ls -a看看目录下有没有.git文件夹,或者直接git init把它变成仓库。
更常见的情况是,你人还在子目录里,但子目录其实不属于任何 Git 仓库的范围内。先cd到仓库根目录,再执行命令。
8.4 push 被拒绝
报错信息通常长这样:
! [rejected] main -> main (fetch first)意思是远程有本地没有的提交。解决办法很简单:先 pull 或 fetch + merge,再 push。
如果 pull 后依旧出现大量冲突,说明两边改动范围比较大,这时要先静下心来一个一个解决冲突文件,不要试图用git push --force绕过,因为没有合理的理由强推其实是危险动作,会覆盖远程提交,影响团队其他人。
8.5 误删或误改代码,想找回
如果改动还在尚未提交的状态,能用git restore找回的部分有限,只有那些你曾经提交过的内容才被 Git 记住。
- 找回误删但尚未 commit 的文件的“上一次提交版本”:
git checkout -- file-name- 找回误删且已经 commit 过的历史版本,可以用
git log找到那个提交的哈希值,然后:
git checkout commit-hash -- file-name所以我总跟团队说,重要的阶段性成果抓紧提交一次,不一定要是一条完整的 feature,哪怕只是“今天早上调通了数据处理逻辑”也可以提交。Git 最大的安全感,恰恰来自一次次及时的提交。
9. 一套我认为合理的基本操作流程
前面讲了这么多零散的命令,最后我结合自己日常开发的节奏,把从开始一个功能到合入主干的流程整理成一套模板,你可以直接照着用。
第一步,在 main 分支上更新到最新:
git checkout main git pull origin main第二步,创建功能分支:
git checkout -b feature-xxx第三步,在功能分支上开发和提交:
git add relevant-file.js git commit -m "feat: 实现 xxx"每完成一个有逻辑意义的阶段就提交一次,不要等到最后再一个巨大的提交。
第四步,把 main 的新改动同步到当前分支(可选但推荐):
git fetch origin git merge origin/main第五步,处理冲突、测试通过之后,推送到远程:
git push -u origin feature-xxx第六步,在远程平台发起合并请求(Pull Request 或 Merge Request),等其他人评审通过后合入 main。
这套流程的核心是:功能分支隔离开发,主干保持稳定,每一个提交有清晰目的。坚持下来,不但自己代码管理得清爽,团队协作成本也会下降一大截。
我在实际工作中最大的体会是,Git 的命令本身并不多,难的是你对“每个操作会对历史记录产生什么影响”有清晰的认识。很多时候,一个命令的误用短期看不出问题,但一周、一个月后,当你需要在复杂历史里定位问题时,那些当初偷的懒都会加倍还回来。认真理解暂存区的作用,规范提交信息,谨慎对待强推和 amend,这些习惯比背全命令列表有价值得多。
最后再分享一个小技巧:如果某天你用 Git 操作时心里发虚,先复制当前目录做个备份,再去执行。这不是胆小,而是资深用户都在用的“安全网”。等你在一个有惊无险的误操作里成功把仓库救回来,你对 Git 的理解会一下子突飞猛进。