Git是程序员绕不开的一道坎。我第一次用Git的时候,光是把代码推到仓库就折腾了快一个下午,最崩溃的是明明照着文章敲了 git push 却一直报错。后来把基本操作系统地过了一遍,又把安装、配置、分支、合并这些容易出问题的点逐个踩过,才算真正把Git用顺手。这篇Git基本操作学习笔记,就是我结合日常工作和线上踩坑整理出来的实战总结,适合刚开始学Git、或者用过一段时间但总被各种异常卡住的开发者。
下面我不会把命令手册搬过来,而是按实际干活时遇到问题的顺序来写:先装好环境,再跑通提交流程,然后处理分支合并,最后把高频报错和解决办法整理成速查表。每一条我都尽量说清楚“为什么要这么做”,因为Git没有真正理解而靠硬背,是后面所有坑的根源。
1. 环境准备与初始配置
1.1 三步装好Git并验证环境
很多人觉得安装Git太简单,不值得写,但我见过太多同事因为安装时勾错了选项,后面在终端和IDE里反复踩坑。这里只讲Windows上最容易出问题的地方,macOS和Linux相对省心一些。
第一步,从Git官网下载Windows安装包。下载速度如果比较慢,可以用国内镜像站,文件名是类似 Git-2.40.1-64-bit.exe 的形式。安装时一路Next,但注意在 Adjusting your PATH environment 这一步,有三个选项:默认的 Git from the command line and also from 3rd-party software 是最推荐的选择,它会把Git的bin目录写进系统PATH,让CMD、PowerShell以及IDEA、VS Code这些工具的内置终端都能直接识别 git 命令。如果选了中间那个 Git from the command line only,有一部分IDE会找不到Git的可执行文件。还有一个容易混淆的地方是行尾转换,默认的 Checkout Windows-style, commit Unix-style 适合大多数团队,如果你的项目是纯Linux部署且团队没有Windows开发者,可以考虑改成 Checkout as-is, commit as-is,避免换行符把diff刷得满屏都是。
第二步,把Git Bash放进右键菜单。这一点很多人不知道,安装时默认 Git Bash Here 和 Git GUI Here 是勾选的,但如果你在安装时取消了,后面可以从开始菜单里的 Git Bash 手动打开。建议保留右键菜单功能,在Windows上进入一个仓库目录最快的方式就是在文件夹空白处点右键选 Git Bash Here。
第三步,验证安装。打开Git Bash或者终端,执行:
bash复制git --version
能打印出版本号,比如 git version 2.40.1.windows.1,环境就算装好了。如果你的系统里同时装了多个Git版本,可以用 which git 看看实际调用的是哪一个,这个排查思路后面经常会用到。
1.2 配置用户信息与SSH免密
装完Git第一件事不是急着clone仓库,而是先配用户名和邮箱。Git的每一次commit都会记录提交者的身份信息,如果不配,提交时要么报错,要么每次用临时账号提交,记录在仓库里非常难看。
bash复制git config --global user.name "你的名字"
git config --global user.email "你的邮箱"
--global 表示全局生效,写入当前用户目录下的 .gitconfig 文件。如果你在某个项目里需要不同的身份,可以在那个仓库目录下不加 --global 再设置一遍,仓库级配置会覆盖全局配置。用 git config --list --show-origin 可以查看当前所有配置分别来自哪个文件,排查配置不生效的诡异问题特别有用。
配好身份信息之后,建议顺手把SSH免密做了。用SSH方式拉代码和推代码,不用每次输入账号密码,而且比HTTPS方式更不容易被token过期卡住。生成密钥的命令是:
bash复制ssh-keygen -t rsa -b 4096 -C "你的邮箱"
一路回车,会在 ~/.ssh 下生成 id_rsa(私钥)和 id_rsa.pub(公钥)。查看公钥内容:
bash复制cat ~/.ssh/id_rsa.pub
把输出的内容整段复制,粘贴到GitHub的 Settings -> SSH and GPG keys -> New SSH key,或者Gitee的 设置 -> SSH公钥 页面。添加完成之后验证:
bash复制ssh -T git@github.com
如果看到类似 Hi yourname! You've successfully authenticated 的输出,说明免密配置成功。这里要特别注意,验证命令里的用户名必须写 git,不是你的GitHub用户名,因为SSH协议内部通过密钥来识别身份。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础操作:把日常提交流程跑通
2.1 从本地文件到远程仓库的完整链条
Git的日常操作其实就一条主流程:工作区改代码 -> 暂存区暂存 -> 本地仓库提交 -> 远程仓库推送。理解这条链,比记住一百个命令更值钱。
新手最常见的疑问是有两种开始方式,到底用哪个。一种是在本地目录执行 git init,这会把当前目录变成一个Git仓库,然后你可以远程加地址:
bash复制git remote add origin git@github.com:用户名/仓库名.git
另一种是 git clone,从已有的仓库拉一份完整副本下来,克隆完自带远程地址,直接开始改:
bash复制git clone git@github.com:用户名/仓库名.git
日常操作开始时,先执行 git status 看当前状态。它会用红字显示修改过但没暂存的文件,用绿字显示已经暂存的文件。修改文件之后,把改动加入暂存区:
bash复制git add .
git add . 会把当前目录下所有未跟踪的和修改过的文件都加进去,简单直接,但也容易把不该提交的文件带进去。如果你只想提交某个文件,用 git add 文件名;只想提交“已经跟踪但删除或修改过的文件”,用 git add -u。我个人的习惯是:小改动直接 git add .,大改动尽量指定文件,这样提交信息能写得精准,回滚也容易。
暂存之后提交:
bash复制git commit -m "feat: 添加用户登录接口"
提交信息这一步,强制建议写清楚“这次改动做了什么”,别写 update、fix bug 这种模糊话。很多团队明文规范提交信息,就是为了让历史可追溯,一条 git log 扫过去就能看出每个commit的意图。
最后推送到远程:
bash复制git push origin master
origin 是远程仓库的默认别名,master 是当前要推送的分支名。如果你的默认分支叫 main,把 master 换成 main 即可。拉取远程改动则是:
bash复制git pull
这里有个很多人没意识到的点:git pull 实际上等于 git fetch 加 git merge,也就是先把远程的最新提交拉到本地,再合并到当前分支。如果你不想让分支分叉得乱七八糟,可以用 git pull --rebase,它会把你本地未推送的提交“叠放”到远程提交之后,历史看着更线性。
2.2 提交信息规范与commit消息改写
提交规范是我踩了半年坑之后才真正重视起来的东西。一个仓库的Git历史,如果每个人都随心所欲地写提交信息,三个月之后你想找某次改动,只能靠 git log --oneline 一条条翻,浪费时间还容易漏。
推荐一套很通用的规范:提交信息第一行用 类型(影响范围): 简要描述 的格式。类型常用这些:
| 类型 | 含义 | 示例 |
|---|---|---|
| feat | 新功能 | feat: 新增订单导出功能 |
| fix | 修复缺陷 | fix: 修复登录超时问题 |
| docs | 文档变更 | docs: 更新README部署说明 |
| refactor | 重构代码,不改功能 | refactor: 抽取公共HTTP工具类 |
| test | 测试相关 | test: 补充购物车单元测试 |
| chore | 构建、依赖等杂项 | chore: 升级Spring Boot版本 |
我这套格式用了三四年,最大的好处是 git log --oneline 出来之后,每个提交干嘛的清清楚楚,Review代码时效率高很多。
再说 git commit --amend 怎么用,这也是被问得很多的命令。它的作用是“改写最近一次提交”。两种典型场景:
场景一,commit信息写错了。比如你写的是 fix: 修bug,想改成 fix: 修复库存扣减错误,执行:
bash复制git commit --amend -m "fix: 修复库存扣减错误"
场景二,提交完发现漏了一个文件。先把漏掉的文件加入暂存区,然后:
bash复制git add 漏掉的文件
git commit --amend --no-edit
--no-edit 意思是不重新编辑commit信息,直接用原来的。
但这里有一条绝对红线:git commit --amend 只适合改写“还没有推送到远程”的本地提交。如果这个commit已经push过,别人可能已经基于它工作了,你amend之后历史变了,强制push会让别人本地直接冲突,这是团队协作事故级别的操作。已经推送的commit想纠正,应该用 git revert 生成一个反向提交,而不是去改历史。
2.3 .gitignore的坑与文件过滤
“git的过滤文件没有作用”这个话题,在搜索引擎的Git相关问题里常年靠前。绝大部分情况不是语法写错了,而是文件已经被Git跟踪了。
.gitignore 的作用是告诉Git“哪些文件我不要跟踪”,但它只对“尚未被跟踪”的文件有效。如果你在项目里提交过一次 config.ini,后来才在 .gitignore 里加了 config.ini,这个文件依然会被Git继续跟踪,因为Git的规则是“已经跟踪的文件不会被忽略”。
解决办法是先解除跟踪,再提交这个变更:
bash复制git rm --cached config.ini
--cached 参数的意思是只从Git的跟踪列表里移除,保留本地磁盘上的文件。执行完这条命令,再配合 .gitignore 里的规则,之后这个文件就真的被忽略了。
.gitignore 的基本语法也不复杂:每一行一条规则;反斜杠开头表示只匹配仓库根目录;行尾加 / 表示匹配目录;* 匹配任意字符;! 开头表示反选,把前面忽略掉的某类文件重新包含回来。常见的忽略项包括 node_modules/、target/、.idea/、.env、*.log 等等。
一个我踩过的坑:某个编译器生成文件加了 .gitignore 也没用,最后发现是因为在加入忽略规则之前,它已经被别人提交到仓库里了。处理方式还是那个:git rm -r --cached 目录名,把整目录从跟踪里移除,再推送一次。
3. 分支操作:合并、切换与代码迁移
3.1 分支创建与切换的核心操作
分支是Git最强大的设计之一,也是新手最容易懵的地方。一句话解释分支的本质:分支就是一个指向commit的指针。你切换分支,本质上是把当前工作对象切换到另一个commit点,同一个仓库里可以有多个分支并行推进,互不干扰。
查看当前仓库的分支列表:
bash复制git branch
带 * 号的就是当前所在分支。创建一个新分支:
bash复制git branch dev
切换到该分支:
bash复制git checkout dev
新版Git推荐用:
bash复制git switch dev
switch 和 checkout 功能一样,但语义更清晰,不会跟 git checkout -- 文件名 的用法混淆。创建并切换一步到位:
bash复制git checkout -b feature/login
这里有个特别重要的概念:切换分支,工作区里的文件会跟着变。比如你在 dev 分支上新建了文件,切到 master 时它会消失,切回来它又出现。这其实是Git帮你把不同分支的工作区隔离了,是好事,但也是冲突的来源。所以我的习惯是,切换分支前一定先 git status 看工作区是否干净,有未提交的改动会横跨分支携带过去,很容易带着脏改动切到其他地方。
为了验证你对分支的理解,可以做一个非常简单但深刻的小实验:创建一个新仓库,在master上提交一个文件,然后切到新分支改内容,再切回master,看看文件内容的变化。亲手操作一遍,比看十篇分支原理的文章都管用。
3.2 合并代码时如何解决冲突
分支建了总要合回来,Git合并的核心命令是:
bash复制git merge dev
这个命令的作用是把 dev 分支的内容合并到当前分支。如果双方各自改了不同文件,Git会自动化完成合并,自动生成一个merge commit。如果两个人改了同一个文件的同一行,Git无法判断该保留谁,就会停下来报冲突。
冲突文件会出现在 git status 里,状态显示为 both modified。打开冲突文件,能看到这样的标记:
code复制<<<<<<< HEAD
当前分支的代码
=======
dev分支的代码
>>>>>>> dev
<<<<<<< HEAD 到 ======= 是当前分支的版本,======= 到 >>>>>>> dev 是待合并分支的版本。解决冲突就是人工决定最终保留哪份、删掉哪些、要不要两者都保留。一个常见原则:如果冲突发生在一个Java方法里,建议打开IDE,把两边代码拖到编辑器里对比逻辑后再决定,不要直接在冲突标记里删删改改,很容易把半截语法弄坏。
编辑完成之后,执行 git add 冲突文件,然后 git commit 或者直接 git merge --continue 完成合并。如果发现冲突解决不了,想回到合并前的状态:
bash复制git merge --abort
这个命令会把工作区恢复到合并开始前。
跟 merge 经常一起出现的是 rebase,这里顺带说清楚区别。git merge dev 会保留两条分支的分叉和汇合点,历史是网状的;git rebase dev 会把当前分支的提交“重放”到 dev 分支最新提交后面,历史是一条直线。直观对比就是:git log --graph 里,merge版本有分支交叉线,rebase版本干净笔直。选哪个取决于团队规范,我个人的做法是:功能分支开发期间,偶尔用 git rebase master 把master上的新改动同步进来,保持分支跟得上主干;合并回master时用 git merge --no-ff dev,保留一个清晰的合并节点,方便回溯。
3.3 把master上的代码“剪切”到dev分支的两种思路
有一个几乎每个团队都会遇到的热词问题:“我在master上写的代码,怎样剪切到dev上?”这里分两种情况讲,因为很多人一开始都搞混。
情况一,代码还没commit。比如你在master上写了一半,突然发现应该在dev分支上做。最简单的方式是:
bash复制git stash
git stash 会把当前未提交的改动“存起来”,工作区立刻干净。然后切到dev:
bash复制git checkout dev
git stash pop
改动就整体恢复到dev分支的工作区了。这个方案适合未提交的一批零散改动。
情况二,代码已经commit了,甚至已经有多个提交。在dev分支上,逐个把这些提交“复制”过来:
bash复制git checkout dev
git cherry-pick 提交hash
git cherry-pick 能把指定commit应用到你当前所在的分支,相当于把它“抄一份”过来。找到commit hash用的命令是:
bash复制git log --oneline
复制全部提交过来之后,如果master上这些提交不该存在,回master把它们删除。如果只想删最近一个提交,用:
bash复制git reset --hard HEAD~1
如果要删多个历史提交,git rebase -i 进入交互式界面来处理。这些都是改写历史操作,原则同前面说的amend:只适合本地还没push的提交,已经push到共享仓库的,优先考虑用 git revert,不要硬写历史。
顺带回答另一个高频问题:git cherry-pick 和 git fetch 的区别。git fetch 是把远程仓库最新提交拉到本地,但只更新远程分支引用(比如 origin/master),不会动你当前的工作区和分支内容。git cherry-pick 是把某个已经存在的commit应用到你当前分支,生成一个新提交。一个负责“同步远程信息”,一个负责“选择性地搬提交”,用途完全不同。
4. 常见问题与排查技巧实录
4.1 SSH认证失败、token配置与remote管理
热词里“ssh认证失败 git”排得很靠前,说明这个坑的覆盖面确实大。SSH认证失败的报错一般是 Permission denied (publickey) 或者 Host key verification failed,排查时按顺序来。
第一步,确认你的密钥存在且路径正确。默认路径是 ~/.ssh/id_rsa,如果你之前生成时改过文件名,比如叫 github_id_rsa,Git默认不会读它,需要在 ~/.ssh/config 里显式配置Host对应的IdentityFile。很多认证失败案例都是这一步翻车。
第二步,确认公钥真的加到平台上了。用 ssh -T git@github.com 验证,如果返回 Permission denied (publickey),回到GitHub设置页检查公钥内容是否完整、有没有多余空格。把 id_rsa.pub 全部拷走,注意开头是 ssh-rsa,结尾是注册时的邮箱。
第三步,加调试参数看具体卡在哪一步:
bash复制ssh -vT git@github.com
输出里会详细显示SSH连接过程中读取了哪个密钥、服务端接受还是拒绝。看到 Server accepts key 就知道是密钥本身没问题,问题在别处。
还有一个常见但容易忽略的点:如果你之前用HTTPS方式操作过仓库,remote地址是 https://github.com/...,那么你配了SSH密钥也没用,SSH只在remote地址是 git@github.com:... 时才生效。修改现有仓库的remote地址:
bash复制git remote set-url origin git@github.com:用户名/仓库名.git
用 git remote -v 查看当前远程地址,确认修改是否生效。
再说token配置。现在GitHub、Gitee都支持用Personal Access Token代替密码。在HTTPS方式下,格式通常是:
bash复制https://用户名:token@github.com/用户名/仓库名.git
但把token明文写进remote地址,等于把密码存进配置文件里,有一定泄露风险。更稳妥的做法是用凭证管理器保存,Windows下Git自带Git Credential Manager,第一次推送让你输入账号和token之后,后续自动使用。用 git config --global credential.helper manager 开启即可。如果你在脚本里需要使用token,推荐放到环境变量里引用,别写进代码仓库。
4.2 大文件提交失败与Windows下Git Bash报错
推送大文件被拒,是很多刚用Git的人的第二道坎。比如往仓库里放一个几百MB的安装包或者视频,执行 git push 时服务端直接报 remote: error: File xxx.zip is 205.65 MB; this exceeds GitHub's file size limit of 100.00 MB。
Git本身作为文本版本控制工具,对大文件很不友好。原因在于每次修改大文件,Git都要存一份新旧差异,仓库体积会迅速膨胀。正规的解决方案是Git LFS(Large File Storage),它把大文件的内容存储在远端单独的大文件服务器上,仓库里只存一个文本指针,大幅降低仓库压力。基本用法:
bash复制git lfs install
git lfs track "*.zip"
执行 git lfs track 之后,其实它会在 .gitattributes 文件里追加一行,提交时把这个文件一起提交,后续 *.zip 后缀的文件就会走LFS通道。适合大文件的场景包括模型文件、音频样本、数据集、设计稿源文件等。
还有一个Windows下特有的诡异报错:git open /dev/null or dup failed: no such file or directory。我最初遇到还以为Git坏了,后来发现多半是文件句柄被其他程序占用,或者杀毒软件在后台扫描Git操作的文件。处理办法:先关闭占用文件的编辑器,再以管理员身份运行Git Bash重试;如果还不行,临时退出安全软件测试;老版本Git也有此bug,更新到新版本基本能解决。这个报错不影响仓库数据安全,处理完就能正常操作。
4.3 用Git恢复误删文件与submodule避坑
“Git目录泄露如何下载”这条热搜词,我在这里必须明确说一句:利用别人仓库意外暴露的 .git 目录下载源代码,属于越权获取他人私密数据的行为,既不合规也不道德,正经用法应该是保护自己的仓库不被误暴露。但如果你自己在操作中误删了工作区文件,.git 目录还在,Git给了你后悔药。
工作区文件误删,最直接的恢复是:
bash复制git checkout -- 文件名
或者用新式写法:
bash复制git restore 文件名
文件会被还原成最近一次commit的状态。如果误删针对的是整个提交,比如执行了 git reset --hard 后发现冲过了头,用 git reflog 可以找回。git reflog 显示的是本地仓库所有HEAD移动的记录,包括reset、merge、checkout等操作。找到你想回到的那个commit hash,执行:
bash复制git reset --hard 对应的hash
这条命令我救过不止一次,特别是reset操作导致丢失未推送提交的时候。但记住,git reflog 记录的有效期大概是几十天,过期后日志会清理,所以要尽早恢复。
再简单说一下submodule。git submodule 是用来在一个仓库里引用另一个仓库的固定版本,典型场景是仓库A要依赖仓库B,但B由另一个团队独立维护。添加子模块:
bash复制git submodule add git@github.com:用户名/子仓库名.git 子目录名
克隆一个带submodule的仓库时,光 git clone 是不够的,子模块目录默认是空的,需要执行:
bash复制git submodule update --init --recursive
这里最容易踩的坑是:当你切换分支或拉取更新后,子模块的指向可能变到别的commit,而代码不会自动更新。团队协作时,如果看到子模块目录内容与预期不符,先 git submodule update 同步一下,再排查是不是某个提交忘了推。
最后分享一个我的个人习惯:每学一个Git命令,先在自己的测试仓库里跑一遍,观察它改变的是什么。比如 git reset 有 --soft、--mixed、--hard 三种模式,区别只在引用、暂存区、工作区三层分别动不动,自己动手跑一圈,比背书管用得多。另外,多留意 git status 和 git log 的输出提示,报错时Git真的会把解决方向写在提示里,只是很多人被那一屏英文唬住了。Git这东西,教程再多,都不如自己亲手踩一次坑来得扎实。
