我见过不少开发者,GitHub 和 Gitee 用了两三年,日常还是只有 clone、add、commit、push 这几个命令。工作流一旦遇到 push 被拒、分支错乱、多平台同步,就只能靠删除目录重新克隆来救命,风险大不说,操作还慢。最近我抽空把这两年在 GitHub 和 Gitee 上维护项目的命令操作完整梳理了一遍,这篇就当作系统掌握 Git 命令的实操笔记。我会从一个项目从初始化、日常提交、分支协作、开源提交、到版本发布的完整链路来展开,每一条命令都会说说它背后的机制、常见的坑和我的建议。适合刚接触 Git 的朋友,也适合用了挺久但还没系统整理过命令细节的开发者。
1. 开始之前:这两项配置没做对,后面全是幺蛾子
很多教程一上来就教 clone、push,但其实影响日常体验最深的,是那几条不怎么起眼的配置命令。配置如果乱了,你可能会遇到:提交记录显示一串乱码一样的人名、每次推代码都要输密码、团队里明明都用 LF 换行符但 diff 看着全是改动。这些问题本质都是环境没铺好。
1.1 用户信息:git config 的生效层级与优先级
Git 每次提交都会把你的姓名和邮箱写进历史记录,所以用户信息配置是第一优先级。这里有三层配置,从系统级到仓库级,作用域越窄优先级越高:
| 层级 | 命令示例 | 生效范围 | 优先级 |
|---|---|---|---|
| 系统级 | git config --system user.name "your-name" |
当前机器的所有用户 | 低 |
| 全局级 | git config --global user.name "your-name" |
当前系统用户 | 中 |
| 仓库级 | git config user.name "your-name" |
当前仓库 | 高 |
最常见的坑是只配了全局用户信息,却在不同平台上用同一个邮箱提交。GitHub 和 Gitee 的账户识别机制都靠邮箱关联提交记录,邮箱不同,你推送上去的提交虽然能推送成功,但头像和个人主页的贡献图不关联,看上去就像别人帮你提交的代码,代码和身份完全对不上。
检查当前配置用 git config --list,想快速看生效的某一个值用 git config user.name。要排查配置问题,我一般直接运行 git config --show-origin --list,它会列出每个配置项的来源文件,能快速定位是哪一层设置出了问题。
1.2 SSH 密钥与 HTTPS:为什么多数老手推荐 SSH
连接 GitHub 和 Gitee 有两种主流方式:HTTPS 和 SSH。HTTPS 推送时需要账号密码或 token,SSH 则基于非对称加密,密钥文件在你的机器上,平台存公钥,推送时不需要频繁输入密码。
单从命令上看,两者只是地址前缀不同:https://github.com/你的名称/仓库名.git 和 git@github.com:你的名称/仓库名.git。但体验差异很大。用 HTTPS 搭配 token 推送,token 有有效期,过期后你得重新生成;SSH 密钥可以长期使用,只要私钥不泄露。所以我个人强烈建议新项目一律走 SSH。
生成密钥的标准流程:
bash复制# 生成 ed25519 密钥,-C 后填邮箱仅做备注标识
ssh-keygen -t ed25519 -C "your-email@example.com"
# 查看公钥内容,复制到平台 SSH 公钥设置里
cat ~/.ssh/id_ed25519.pub
有一个细节容易被忽略:ssh-keygen 可以指定文件名,如果你有多个平台密钥,建议不要全部留在默认的 id_ed25519 上。比如:
bash复制ssh-keygen -t ed25519 -C "comment" -f ~/.ssh/gitee_id_ed25519
然后在 ~/.ssh/config 里给不同域名指定不同的密钥文件,否则你可能有密钥加载错位的尴尬。
code复制Host gitee.com
HostName gitee.com
User git
IdentityFile ~/.ssh/gitee_id_ed25519
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/github_id_ed25519
配置完成后用 ssh -T git@github.com 和 ssh -T git@gitee.com 测试连通性。看到“Hi xxx! You've successfully authenticated”就说明没问题。
1.3 换行符与文件权限的隐藏坑
Windows 上开发的痛点之一就是换行符。Windows 用 CRLF,Linux 和 macOS 用 LF。Git 默认会按平台的 core.autocrlf 策略做转换,但这也会带来莫名其妙的“整个文件被改动”问题。
我的建议是团队统一用一套规则,在仓库根目录放 .gitattributes 文件比让每个人单独配置更可靠:
text复制* text=auto
*.sh text eol=lf
*.bat text eol=crlf
这样无论是谁在什么系统上提交,Git 都能按规则统一处理换行符。text=auto 让 Git 自己判断文本类型,.sh 强制 LF,.bat 强制 CRLF。这个文件提交到仓库后对所有人生效,比每个开发者的本地配置可控制性强得多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 日常三大命令:clone、commit、push 的底层动作拆解
2.1 clone 远不止"下载一份代码"这么简单
git clone 把远端仓库的代码、分支、标签、提交历史全部拉到本地,同时自动建立本地分支与远端分支的追踪关系。最基本的用法:
bash复制git clone git@github.com:your-name/repo-name.git
如果你想拉下来后直接进入某个分支:git clone -b dev git@github.com:your-name/repo-name.git。如果是大型仓库,只想要最近一次提交,不想把完整历史都拖下来,用 --depth 1:
bash复制git clone --depth 1 git@github.com:your-name/repo-name.git
浅克隆在早期很实用,但要注意它截断历史,后续提交等操作基本不受影响,可如果你需要基于旧版本排查问题或看完整历史,就会受限。还有一个冷门但好用的参数 --recurse-submodules,当项目里有 Git Submodule 时,加这个参数会连同子模块一起克隆,能省去后面单独初始化和更新的步骤。
另外,clone 下来之后第一件事,我会先看一眼 git remote -v,确认远端地址里有没有问题。很多人以为 clone 完就万事大吉,实际工作中因为远端地址被非法修改或 graf 配置异常导致 push 跑到错误仓库的情况并不少。
2.2 暂存区与 commit:add 之前先想清楚一件事
Git 的工作区、暂存区、版本库三层模型是很多人一开始没搞明白的地方。简单理解:工作区是你肉眼看到的文件,暂存区是你要纳入下一次提交的候选清单,版本库是每次 commit 形成的历史记录。
git add 就是把工作区改动放进暂存区。这里我的习惯是尽量不用 git add . 一把梭。因为 git add . 会把当前目录下所有新增、修改、删除的文件全部加进去,万一里面有临时文件、本地配置、调试日志,就会被不小心提交并推送。我推荐的做法:
bash复制# 先查看状态
git status
# 逐个添加必要文件,或按目录添加
git add src/main/java/com/example/service/UserService.java
git add docs/
提交信息的规范同样重要。git commit -m "fix: 修复登录超时导致的白屏问题" 这种带类型前缀的写法,团队比较常用,也是社区的主流风格。虽然这只是约定,但好处很明显:历史一眼能看出每条提交是功能、修复、重构还是文档,配合后续的 git log --oneline,效率远超“更新代码”“修改bug”这种无意义信息。
提交时还有一个省力技巧:如果只是想提交已有暂存区中的所有改动,不追加新改动,用 git commit 就好,它默认把暂存区内容提交。如果想跳过暂存区,直接提交所有已追踪文件的改动,可以 git commit -am "message",但这个命令不会把新增文件加入版本控制,只对已跟踪文件的修改和删除生效,所以别指望它当 git add 的万能替代。
2.3 push 被拒:fetch、pull、rebase、merge 的正确出场顺序
push 被拒是新手遇到最多、觉得最恐怖的场景,报错信息就一句话:Updates were rejected because the remote contains work that you do not have locally. 翻译成人话就是:远端分支上有你本地没有的提交,Git 禁止用你的历史覆盖远端历史。
很多人第一反应是 git pull。pull 其实是两步操作的组合:fetch + merge。fetch 只把远端最新的提交拉到一个本地引用里,不碰你的工作区;merge 把远端分支合并进当前分支。
而推荐的做法通常是把 pull 改成:
bash复制git pull --rebase
rebase 会把你的本地提交先放到一边,把远端新提交拉下来,再把你的提交一个个重放到最新提交之上,形成一条线性历史。相比 merge 会产生一个“合并提交”,rebase 之后的历史清爽很多。
但这里有个必须强调的坑:如果你已经把本地提交推送到过远端,就不建议再用 rebase 改写历史,因为改写后的提交哈希变化,远端已有的记录和本地就不再一致,下一次 push 依然会被拒,而且别人可能已经基于旧提交做了合并。经过 rebase 的提交需要强制推送才能覆盖远端,这必须非常小心,稍有不慎就会产生不可逆转的分叉。
push 被拒时的处理顺手写一下:
bash复制# 1. 拉取远端最新提交,用 rebase 方式
git pull --rebase
# 2. 如果有冲突,修完冲突后继续
git add .
git rebase --continue
# 3. 推送到远端
git push
如果 rebase 中途发现不好处理想放弃,用 git rebase --abort 可以回到 rebase 之前的状态,这点建议记下来,安全感会强很多。
3. 分支操作:多人协作最容易翻车的地方
3.1 分支的创建、切换与命名习惯
分支是 Git 最强大的设计之一,也是初学者最容易搞混的地方。分支本质上是一个可移动的指针,指向某条提交链的最新提交。创建分支的命令:
bash复制# 基于当前 HEAD 创建新分支
git branch feature/login
# 切换过去
git checkout feature/login
# 或直接一步完成
git checkout -b feature/login
# 新版 Git 也支持 switch 命令
git switch -c feature/login
我推荐团队用 feature/功能名、fix/问题描述、docs/说明 这种命名模式。功能分支独立开发,互不干扰;主干分支保持稳定,可随时发布。这个模式在 GitHub 和 Gitee 上都适用,也和平台的 PR 机制天然契合。
合并到主干前通常先确认目标分支:
bash复制git checkout main
git pull --rebase
git merge feature/login
有一个经验:长期分支用 merge,短期分支用 rebase 之后再合并。长期分支如果有频繁并行开发,merge 能保留完整的合流信息;短期功能分支 rebase 之后再 merge,历史更干净。太教条反而容易让团队在指令上纠结,形成一种团队内的统一习惯即可。
3.2 合并冲突:不是 bug,是正常的协作流程
冲突的本质是两个人改了同一处代码,Git 不知道保留谁,只能让你来判断。遇到冲突先别慌,这不是代码坏了,反而是 Git 在保护历史不被错误覆盖。
冲突发生时,文件里会出现类似这样的区块:
text复制<<<<<<< HEAD
这里是当前分支的代码
=======
这里是待合并分支的代码
>>>>>>> feature/login
手动解决就是把不需要的部分删掉,保留合并后的正确版本,然后把 <<<<<<<、=======、>>>>>>> 这些标记清理干净。解决完成:
bash复制git add .
git commit -m "merge: 合并 feature/login 到 main,解决登录接口冲突"
我处理冲突的习惯是尽量用 IDE 的合并工具,比如 VS Code 的源代码管理视图,直接提供“采用当前更改”“采用传入更改”“两侧都保留”几个选项,比在文本编辑器里肉眼找分割线高效很多。还要提醒一句:解决冲突前,先把 git log --oneline -5 看一下现场,避免误操作把别人分支的提交丢掉。
3.3 本地分支与远端分支的"隐形皮带":tracking 关系
本地分支和远端分支之间的联系,Git 内部叫“追踪关系”。当你 git clone 一个仓库时,本地的 main 分支会自动追踪远端的 origin/main,所以 git pull 和 git push 不用指定远端和分支。但你自己创建的分支,默认没有这个联系,第一次推送时 Git 会提示:
bash复制git push -u origin feature/login
-u 的全称是 --set-upstream,它会把本地分支和 origin/feature/login 关联起来。这行命令执行后,后续推送就只用 git push 了。查看现有追踪关系:
bash复制git branch -vv
如果看到本地分支处于“无追踪”状态,或者想修改追踪目标,可以用:
bash复制git branch -u origin/main
这个追踪关系影响的不只是 push 和 pull,还包括 git status 里“你的分支领先/落后远端 xx 个提交”的显示。很多人在团队协作中搞不清楚为什么提示落后于远端,其实就是因为追踪关系没对上或者滞后没同步。
4. GitHub 与 Gitee 双线作战:remote、PR 与多平台同步
4.1 remote 远端管理:从克隆到多远端推送
Git 支持一个仓库配置多个远程地址,这是很多人没充分利用的能力。比如你在 Gitee 上也同步一份同一套代码,就无需单独维护两个目录。
添加远程地址的命令:
bash复制git remote add gitee git@gitee.com:your-name/repo-name.git
这样本地默认的 origin 指向 GitHub,额外的 gitee 指向 Gitee。推送时你可以选择一次性推多个:
bash复制# 推送到两个远端
git push origin main
git push gitee main
# 或者更暴力一点的写法,修改推送 URL,让 origin 同时推到两个平台
git remote set-url --add origin git@gitee.com:your-name/repo-name.git
第二种写法会让 git push origin main 同时把代码推到 GitHub 和 Gitee。好处是省事,坏处是一旦一个有权限问题,整个 push 会报错,反馈不直观。我自己日常用的是第一种,分开推,出问题方便定位。
查看远端信息:
bash复制git remote -v
git remote show origin
git remote show origin 会列出远端分支的详细状态,包括远端有哪些分支、本地哪些分支已经过时、哪些远端分支已经删除,这个命令在协作前检查环境非常实用。
4.2 命令行视角下的 Pull Request 流程
GitHub 的 Pull Request 和 Gitee 的 Pull Request 机制本质一致:你 fork 或基于分支修改,发一个请求让原仓库作者合并你的代码。大部分 PR 操作在网页端点击就能完成,但真正高效的做法是命令行配合。
标准参与流程:
bash复制# 1. fork 别人的仓库后,clone 自己的仓库地址,并把原仓库设为 upstream
git clone git@github.com:your-name/某开源项目.git
git remote add upstream git@github.com:原作者的仓库地址.git
# 2. 拉取上游最新代码
git fetch upstream
# 3. 基于上游最新代码创建自己的功能分支
git checkout -b fix-login -t upstream/main
这里关键是切换到 fix-login 分支,然后在其上修改。开发完成后推送:
bash复制git push origin fix-login
接着打开 GitHub 或 Gitee 页面,创建一个面向原仓库的 PR。在原仓库有新动静、你需要同步时,可以直接:
bash复制git pull --rebase upstream main
这种“upstream 拉取 + 本地 rebase + 强制推送更新 PR 分支”的做法在开源社区里很常见,保持 PR 历史的整洁性。它要求你对 rebase 有把握,否则建议直接 merge,用额外的合并提交同步远端变化。
PR 审核环节有个命令行利器:git checkout origin/pr编号 可以直接切到 PR 对应的代码上测试,在 Gitee 上也支持类似方式,Gitee 的 PR 引用路径一般是 refs/pull/编号/head。在大型协作中,直接在本地复现 PR 分支比在网页端看文件 diff 更能发现潜在问题。
4.3 两个平台使用中的具体差异与坑
虽然命令层面 GitHub 和 Gitee 大同小异,但实际应用中还是有不少差异。
| 对比项 | GitHub | Gitee |
|---|---|---|
| 默认分支名 | main | master(新仓库多数是 master) |
| 私有仓库免费额度 | 私有仓库免费 | 私有仓库也免费,但容量和人数组件有差异 |
| 代码评审工具 | 内置 PR Review,支持代码建议 | 同样支持 PR Review,但我体验下来建议框功能没那么灵敏 |
| 镜像同步 | 需要单独配置 Actions | 提供仓库镜像,可较方便同步 GitHub 仓库 |
| 大文件支持 | 有 Git LFS 支持 | 也有 LFS,但套餐额度差别大 |
最影响命令操作的一个差异是默认分支名。如果你从 Gitee 克隆老仓库,本地默认分支常是 master;从 GitHub 新仓库克隆,默认是 main。这本身不影响命令,但你写自动化脚本、CI 配置时,如果写死了分支名就很容易踩坑。所以我做多平台同步时一般先 git branch -a 确认两边的分支名。
另一个细节是:GitHub 仓库对文件名大小写是敏感服务器配置,老仓库还会缓存大小写,有时改一个文件名大小写后 push 就会发现远端没有变化,这是 Git 默认忽略大小写变化的问题。处理办法是改完文件名后执行 git rm --cached 原文件 再重新添加,或者立即确认 git status。
还有一点,国内访问 GitHub 在特定网络环境下 clone 和 push 经常慢或中断,Gitee 的体验相对稳定。如果你的代码需要同时在两个平台维护,建议推送策略上以 Gitee 为主仓库,配合定时同步到 GitHub,避免频繁在 GitHub 上做大推送。
5. 用顺手之后才值得学的三个进阶方向
5.1 log 查询:从看记录到找线索
git log 是查询历史的核心命令,但默认输出太啰嗦。我常用的几条:
bash复制# 一行显示一条提交
git log --oneline
# 图形化显示分支合并关系
git log --graph --decorate --oneline --all
# 查看某个文件的历史
git log --oneline -- 文件名
# 按作者/日期过滤
git log --author="your-name" --since="2 weeks ago"
git log --all 是不容易记住但很有用的参数,它会显示所有分支、标签、远端分支的提交,不会因为你当前分支的不同而漏掉线索。排查“我有一个提交找不到了”的情况几乎都要用到它。
还有一个非常实用的搜索命令:git log -S"关键词",它搜索的是代码行内容的增删变化。比如你想知道某句话是什么时候被删掉的,git log -S "importantFunction" --oneline 直接给你列出与之相关的所有提交,这类操作在很多团队里比人肉看代码注释靠谱得多。
5.2 撤销的四种姿势:reset、revert、checkout、restore
撤销操作绝对是高频需求,也是搞脏仓库的源头。我根据场景做一个简单划分:
| 场景 | 推荐命令 | 影响范围 |
|---|---|---|
| 修改了文件但没 add | git restore 文件 或 git checkout -- 文件 |
工作区 |
| 已经 add 还没 commit | git restore --staged 文件 |
暂存区 |
| 已经 commit 但还没 push | git reset --soft HEAD~1 |
回退提交 |
| 已经 push 到远端,且多人共享 | git revert 提交哈希 |
生成新的反向提交 |
| 想彻底丢弃某段历史且确认没人用 | git reset --hard 目标哈希 |
危险操作,慎用 |
git revert 和 git reset 是很多人搞混的重点。revert 不是回退历史,而是“反向应用”一次提交,生成一条新提交来抵消旧提交的改动。这样历史链没有被破坏,已有提交的哈希不变,所以多人协作时它是安全的。
reset --hard 则会把当前分支指针直接移动到目标提交,同时丢弃目标之后的所有提交、暂存区内容和工作区改动。这个命令一旦执行,那些丢弃的提交如果没有被任何分支引用,就只能靠 git reflog 找回来。万一误用,别急:
bash复制git reflog
reflog 记录了本地 Git 每次移动 HEAD 的操作记录,能找到你执行 reset 之前的哈希,再用 git reset --hard 之前哈希 抢救回来。这句话希望你们永远用不上,但真的能救仓库。
5.3 tag 与 release:版本发布的最后一块拼图
日常开发中,git tag 的使用频率不算高,但一旦涉及版本发布,它就是必需品。tag 本质是一个指向特定提交的静态引用,不像分支会随提交移动。规范做法是在合并完成、测试通过后打标签:
bash复制# 打一个附注标签,推荐这种方式
git tag -a v1.0.0 -m "release: 首个稳定版本"
# 推送到远端
git push origin v1.0.0
# 批量推送所有本地标签
git push --tags
# 删除本地和已推送的标签
git tag -d v1.0.0
git push origin --delete v1.0.0
为什么推荐 -a 附注标签而不是默认的轻量标签?因为附注标签包含打标签的人、时间、日志,这些信息在发布审计时非常有价值。轻量标签只是一个指针,适合临时标记某次提交,不适合做正式版本发布。
发布版本后,GitHub 和 Gitee 的 Release 页面都会显示你推送的标签,你可以把变更日志、安装包链接挂上去。通常我的流程是:合并功能到主干 → 更新版本号和变更日志 → push 到两个平台 → 打标签 → 在 Release 页面填写说明。标签打错位置了也不用慌,删除后从新位置重新打即可。
我在实际使用中发现,分支管理不乱、版本标记清晰的项目,历史溯源特别快。很多开发者花大量时间纠结“代码该怎么写”,却忽略了“代码怎么被组织、被记录”同样重要。Git 命令不是背出来的,是遇到一个场景用一次,用熟了才真正变成自己的东西。我的建议是:先把这个仓库本身的 remote、branch、log、tag 状态看明白,再逐步尝试 git fetch、git rebase、git reflog 这些进阶命令。平时我也会偶尔用 git fsck --lost-found 检查一下有没有悬空的对象,深挖之后你会发现自己对版本控制的理解明显上了一个台阶。
