1. 项目概述:为什么每个开发者都绕不开Git?
如果你刚入行,或者正准备从其他版本控制系统切换过来,听到“Git”这个词可能会觉得既熟悉又陌生。熟悉是因为几乎每个技术岗位的招聘要求里都写着“熟悉Git”,陌生是因为打开命令行,面对那一堆git init、git commit、git push,感觉像在学一门新外语。我刚开始接触时也这么觉得,甚至一度觉得用图形化工具拖拽文件就挺好。但踩过几次坑,经历过代码丢失、版本混乱、团队协作冲突后,我才彻底明白:Git不是可选项,而是现代软件开发的“水和电”,是每个开发者必须掌握的核心生存技能。
简单来说,Git是一个分布式版本控制系统。别被“分布式”和“版本控制”这些术语吓到。你可以把它想象成一个超级智能、永不丢失的“文件时光机”和“团队协作白板”。它最核心的价值是记录变化。你写的每一行代码、每一次修改、每一次尝试,Git都能帮你完整地记录下来。你可以随时回到过去的任何一个“快照”,对比不同版本间的差异,甚至开一个“平行宇宙”(分支)去试验新功能,而不用担心搞砸主线。在团队里,它更是协作的基石,让多个人在同一份代码上工作而不至于乱成一团。
网上关于Git的教程多如牛毛,从“5分钟入门”到“高级原理剖析”应有尽有。但很多新手教程要么过于简略,只教几个命令,遇到实际问题就抓瞎;要么一上来就大谈特谈“有向无环图”、“快照与差异”,让人望而却步。这篇文章,我想从一个有十多年一线经验的开发者视角,跟你聊聊Git到底该怎么用。我不会只给你命令列表,而是会结合我踩过的无数个坑,告诉你每个命令背后的“为什么”,以及在实际项目中如何组合运用它们来解决真实问题。无论你是想从零开始安装配置,还是已经会用但总在某些地方卡壳,相信都能在这里找到答案。
2. Git的核心概念与工作流拆解
在动手敲命令之前,花点时间理解Git的几个核心思想,能让你后面的学习事半功倍,遇到问题时也知道该往哪个方向思考。很多人觉得Git难,就是因为没搞懂它底层的工作模型。
2.1 仓库、工作区、暂存区与版本库
这是Git最基本也是最核心的模型,必须刻在脑子里。你可以想象自己在一个摄影棚里工作。
- 工作区 (Working Directory):就是你电脑上能直接看到、编辑文件的这个文件夹。就像摄影棚里散落的各种道具、布景和演员,它们处于最原始、未整理的状态。你在这里新增、修改、删除文件。
- 暂存区 (Staging Area / Index):这是一个非常关键的概念,也是Git区别于其他版本控制系统的一大特色。你可以把它想象成摄影棚里的一个“准备台”。当你觉得某个文件的修改已经完成,准备纳入下一次“历史快照”时,你就把它从工作区“搬”到这个准备台上。暂存区允许你精细地控制哪些修改要进入下一个版本,而不是一次性提交所有改动。
- 版本库 (Repository):这是Git的“保险库”或“历史档案馆”。当你把暂存区里准备好的所有改动,打包成一个“快照”并保存时,这个快照就被永久地存入了版本库。每次保存都会生成一个唯一的“提交记录”,里面包含了作者、时间、说明和指向父提交的指针。版本库通常位于你项目根目录下的隐藏文件夹
.git中。
整个流程就是:工作区 -> (git add) -> 暂存区 -> (git commit) -> 版本库。理解了这个流程,你就理解了git add和git commit这两个最基础命令的真正含义。
2.2 提交、分支与标签
- 提交 (Commit):版本库里的每一个“快照”就是一个提交。每个提交都有一个用SHA-1算法生成的40位哈希值(如
a1b2c3d...),作为其唯一ID。提交不是简单地存储文件差异,而是存储了整个项目在某个时刻的完整“快照”(实际上是通过巧妙的数据结构实现的,效率很高)。提交之间通过指针连接,形成一条历史线。 - 分支 (Branch):这是Git的“杀手级”功能。分支本质上只是一个指向某个提交的、可移动的指针。默认情况下,Git会创建一个叫
main(旧版本可能是master)的主分支。当你创建一个新分支(如feature/login)时,你只是创建了一个新的指针,指向当前的提交。之后你在这个分支上的所有新提交,都只移动这个新分支的指针,而main分支的指针原地不动。这就好比从历史主线分出了一条支线,你可以在支线上大胆实验,完全不影响主线。实验成功后再把支线合并回主线即可。 - 标签 (Tag):标签像一个“里程碑”书签,它指向某个特定的提交,并且通常不会移动。常用于标记重要的版本节点,如
v1.0.0、v2.1.0-release。
2.3 分布式 vs 集中式
这是Git的另一个核心优势。像SVN这样的集中式版本控制系统,只有一个中央服务器保存所有版本历史。开发者需要联网才能提交,如果服务器挂了,大家都没法工作。而Git是分布式的,每个开发者的本地电脑上都有一个完整的版本库,包含了全部的历史记录。这意味着你可以在飞机上、在没有网络的环境下,自由地进行提交、创建分支、查看历史。团队协作时,大家通过推送和拉取操作来同步彼此的更改。这种模式不仅更健壮,也使得工作流更加灵活。
3. 从零开始:Git的安装、配置与第一个仓库
理论说再多,不如动手做一遍。我们从最开始的安装配置讲起,确保你的环境是正确可用的。
3.1 跨平台安装指南
Git官方支持Windows、macOS和Linux。我强烈建议从官网下载安装,这是最稳妥的方式。
Windows:
- 访问 git-scm.com 下载Windows安装程序。
- 运行安装程序,在“选择组件”步骤,务必勾选“Git Bash Here”和“Git GUI Here”,这会在右键菜单添加快捷入口,非常方便。
- 在“选择默认编辑器”步骤,如果你不熟悉Vim,强烈建议选择你常用的编辑器,比如VS Code(需要提前安装好)或Notepad++。避免选择Vim导致后续提交时卡在编辑器界面不知所措。
- 在“调整PATH环境”步骤,选择第二项“Git from the command line and also from 3rd-party software”,这会将Git添加到系统PATH,让你能在任何命令行窗口(如CMD、PowerShell)中使用。
- 其他步骤保持默认即可。安装完成后,在开始菜单找到“Git Bash”,这是一个模拟Linux环境的终端,非常适合运行Git命令。
macOS:
- 最简单的方法是安装Xcode Command Line Tools。打开终端,输入
xcode-select --install,按提示操作即可。 - 或者使用Homebrew这个包管理器:
brew install git。
- 最简单的方法是安装Xcode Command Line Tools。打开终端,输入
Linux (Ubuntu/Debian): 使用包管理器安装:
sudo apt update && sudo apt install git。
安装完成后,打开终端(Windows用Git Bash,macOS/Linux用系统终端),输入git --version,如果显示版本号(如git version 2.39.2),说明安装成功。
3.2 必不可少的初始配置
安装完第一件事不是git init,而是配置你的身份信息。这个信息会写入你每一次提交记录,是团队协作中识别作者的关键。
# 设置你的用户名和邮箱(请使用你真实的、常用的邮箱) git config --global user.name "你的名字" git config --global user.email "你的邮箱@example.com"--global参数表示这是全局配置,对这台电脑上所有的Git仓库生效。你也可以在某个特定仓库目录下不加--global进行配置,优先级更高。
注意:这个邮箱最好与你后续使用的代码托管平台(如GitHub、GitLab)的注册邮箱一致,这样平台才能正确地将你的提交与账户关联,显示你的头像和贡献图。
还有一些提高效率的配置:
# 让命令行输出带颜色,更容易阅读 git config --global color.ui auto # 设置默认分支名为 main(更中立的名称) git config --global init.defaultBranch main # 为常用命令设置别名,比如用 `git co` 代替 `git checkout` git config --global alias.co checkout git config --global alias.br branch git config --global alias.ci commit git config --global alias.st status你可以用git config --list查看所有配置。
3.3 创建你的第一个Git仓库
有两种常见场景:初始化一个新项目,或者获取一个已存在的项目。
场景一:本地初始化新仓库假设你有一个项目文件夹my-project。
cd /path/to/my-project # 进入你的项目目录 git init执行git init后,当前目录下会生成一个隐藏的.git文件夹,这就是Git的版本库。此时,你的项目文件都还只在“工作区”。
场景二:克隆远程已存在仓库这是参与开源项目或加入团队项目最常用的方式。
# 从GitHub克隆一个仓库 git clone https://github.com/username/repository.git # 克隆到指定目录 git clone https://github.com/username/repository.git my-local-foldergit clone命令做了几件事:1. 在本地创建以仓库名命名的文件夹;2. 初始化.git目录;3. 拉取远程仓库的所有数据(所有分支、提交历史);4. 自动创建一个跟踪远程main分支的本地main分支。
现在,你的本地环境已经就绪,一个Git仓库也准备妥当。接下来,我们要开始真正的“版本控制”之旅了。
4. 日常开发循环:核心命令详解与最佳实践
掌握了基本概念和环境,我们就可以进入Git的日常使用环节了。这部分是使用频率最高的命令集合,我将结合具体场景和常见陷阱来讲解。
4.1 状态查看与文件追踪
在开始修改前,养成先看状态的好习惯。git status是你的雷达。
git status它会告诉你:
- 当前在哪个分支。
- 本地分支与远程分支的同步情况。
- 工作区中哪些文件被修改了但还没添加到暂存区(显示为红色“Changes not staged for commit”)。
- 暂存区中哪些文件已准备好等待提交(显示为绿色“Changes to be committed”)。
- 哪些是未被Git追踪的新文件(显示为红色“Untracked files”)。
实操心得:git status -s或git status --short会以更紧凑的格式输出,适合快速浏览。两栏输出,第一栏表示暂存区状态,第二栏表示工作区状态。??表示新文件,M表示修改,A表示新增,D表示删除。
4.2 添加改动到暂存区
当你修改了文件,并觉得这部分改动可以作为一个逻辑单元保存时,就使用git add。
# 添加单个文件 git add README.md # 添加所有当前目录下的改动(包括新增、修改,但不包括删除) git add . # 添加所有类型的改动(新增、修改、删除) git add -A 或 git add --all # 交互式添加,可以让你选择文件中的部分改动(非常有用!) git add -pgit add -p(patch模式)是我极力推荐的高级技巧。它会把每个文件的每一处改动单独展示出来,询问你是否要将其加入暂存区。这让你可以精心构造每一次提交,使提交历史清晰、原子化。比如你修复了一个bug同时顺手改了代码格式,就可以用这个命令只提交bug修复的部分,把格式调整留到下一次提交。
注意:
git add .只会添加当前目录及其子目录的改动。而git add -A会添加整个仓库的所有改动,无论你在哪个子目录下执行。根据你的需求选择。
4.3 创建提交:保存历史快照
暂存区准备好后,就可以创建提交了。
# 最常用的提交方式,会打开默认编辑器让你编写提交信息 git commit # 附带简短提交信息的一行式提交(适合小改动) git commit -m "修复了用户登录时的空指针异常" # 添加所有已跟踪文件的改动到暂存区并提交(跳过 git add 步骤) git commit -a -m "提交信息"git commit会打开你配置的默认编辑器(如VS Code、Vim)。提交信息的第一行是标题(摘要),应简短精炼(建议50字符以内)。空一行后是详细描述,可以说明为什么修改、怎么修改、以及可能的影响。
提交信息规范:好的提交信息是项目历史的宝贵财富。我推荐遵循类似Angular的规范:
<类型>(<作用域>): <主题> <正文> <脚注>例如:
fix(auth): 修复登录令牌过期时间计算错误 在JWT令牌生成逻辑中,过期时间`exp`应使用UTC时间戳,原代码错误地使用了本地时间戳,导致令牌过早失效。 Closes #123常见的类型有:feat(新功能)、fix(修复bug)、docs(文档)、style(代码格式)、refactor(重构)、test(测试)、chore(构建过程或辅助工具变动)。
4.4 查看历史与差异
提交之后,如何回顾历史?
# 查看简洁的提交历史(单行显示) git log --oneline # 查看带分支图的历史(非常直观) git log --oneline --graph --all # 查看最近3次提交 git log -3 # 查看某个文件的修改历史 git log -p README.md # 查看工作区与暂存区的差异 git diff # 查看暂存区与最新提交的差异 git diff --staged 或 git diff --cached # 查看两次特定提交之间的差异 git diff commit_id_1 commit_id_2git log --oneline --graph --all是我最常用的命令,它能以图形化的方式展示所有分支的演进和合并关系,一目了然。
4.5 撤销与回退操作
人总会犯错,Git提供了多种“后悔药”,但用法不同,需要谨慎选择。
场景一:撤销工作区的修改(还没git add)
# 撤销对某个文件的修改,危险!会丢失所有未暂存的改动。 git checkout -- filename # 更安全的做法:先暂存,再恢复(利用暂存区作为备份) git add . # 先把所有改动暂存 git stash # 将暂存区的改动储藏起来(后面会讲) git checkout -- . # 此时工作区干净了,可以安全地 checkout # 如果需要恢复,再用 `git stash pop`场景二:撤销暂存区的修改(已经git add了,但还没git commit)
# 将文件从暂存区移回工作区,但保留工作区的修改内容 git reset HEAD filename这个命令很安全,它只是把文件从“准备提交”的状态,变回“已修改但未准备”的状态。
场景三:撤销提交(已经git commit了)这里分两种情况:
软重置 (soft reset):撤销提交,但保留工作区和暂存区的改动。相当于“我提交错了,但改动的代码我还想留着重新提交”。
git reset --soft HEAD~1 # HEAD~1 指向上一次提交执行后,上一次提交被取消,但你的所有修改都还保留在暂存区,你可以修改后重新提交。
混合重置 (mixed reset,默认):撤销提交,并且将改动移出暂存区,放回工作区。相当于“我提交错了,代码改动我要,但我想重新组织一下再提交”。
git reset HEAD~1 # 等同于 git reset --mixed HEAD~1硬重置 (hard reset):危险!撤销提交,并且丢弃工作区和暂存区的所有相关改动。相当于“这个提交以及相关的所有修改,我都不要了”。
git reset --hard HEAD~1警告:
git reset --hard会永久丢弃未提交的改动,且如果已经推送到远程仓库,会给团队协作带来麻烦。仅在确定不需要这些改动时使用。
场景四:已经推送到远程仓库的提交如何撤销?如果错误的提交已经git push了,为了不破坏团队其他人的历史,通常不建议使用git reset。应该使用git revert。
# 创建一个新的提交,来抵消指定提交的改动 git revert commit_idgit revert是安全的,因为它不会重写历史,而是新增一个提交。团队其他成员拉取代码后会自动应用这个“反向”提交。
5. 分支管理:高效并行开发的基石
分支是Git的灵魂,能否用好分支,直接决定了个人和团队的开发效率。
5.1 分支的创建、切换与合并
# 查看所有分支(当前分支前有*号) git branch # 查看所有分支及最后提交信息 git branch -v # 查看包含远程跟踪分支的所有分支 git branch -a # 创建新分支 git branch feature-awesome # 创建并切换到新分支 git checkout -b feature-awesome # 或使用更语义化的新命令 git switch -c feature-awesome # 切换到已有分支 git checkout main git switch main # 删除已合并的分支(安全) git branch -d feature-awesome # 强制删除分支(即使未合并) git branch -D feature-awesome # 将指定分支合并到当前分支 git merge feature-awesome最佳实践:
- 主分支(main/master)保持稳定:这个分支的代码应该始终是可部署、可运行的。所有新功能开发、bug修复都不直接在这个分支上进行。
- 功能分支开发:每开发一个新功能或修复一个bug,都从
main分支拉出一个新的功能分支(如feature/user-auth、fix/login-bug)。在这个分支上独立开发、测试。 - 分支命名清晰:使用
feature/、fix/、hotfix/、release/等前缀,一目了然。 - 频繁合并主分支:在功能分支开发期间,定期将
main分支的更新合并到自己的分支,减少最终合并时的冲突。
5.2 合并与变基:两种整合历史的方式
当功能开发完成,需要将分支合并回main时,有两种主要方式:merge和rebase。
合并 (Merge):这是最直接的方式。它会在历史中创建一个新的“合并提交”,有两个父提交,明确记录了分支汇合的事实。历史呈现树状结构。
git checkout main git merge feature-awesome优点:保留了完整的历史上下文,操作简单安全。缺点:如果分支很多且活跃,历史线会变得非常复杂,像一团乱麻。
变基 (Rebase):一种“重演”提交的操作。它会把当前分支的提交“嫁接”到目标分支(通常是
main)的最新提交之后,使得历史看起来像一条直线。git checkout feature-awesome git rebase main # 变基完成后,再切回main合并(此时会是快进合并) git checkout main git merge feature-awesome优点:历史清晰、线性,更容易追踪。缺点:重写了提交历史。如果这个分支已经推送到远程并被其他人使用,变基会导致他们的历史与你冲突,需要强制推送,给协作带来麻烦。
黄金法则:只对尚未推送到远程的本地提交进行变基。永远不要对已经公开(推送到远程)的提交进行变基。
5.3 解决合并冲突
合并或变基时,如果两个分支修改了同一文件的同一区域,Git无法自动决定保留哪个,就会产生冲突。
当冲突发生时,Git会标记出冲突的文件。打开这些文件,你会看到类似这样的标记:
<<<<<<< HEAD 这是当前分支(例如main)的内容 ======= 这是要合并进来的分支(例如feature)的内容 >>>>>>> feature解决步骤:
- 不要慌。冲突是协作中的正常现象。
- 用编辑器打开冲突文件,仔细分析
<<<<<<<,=======,>>>>>>>标记之间的内容。 - 与相关同事沟通,决定保留哪一部分,或者进行整合修改。删除所有冲突标记。
- 修改完成后,将解决好的文件添加到暂存区:
git add conflicted_file.py。 - 完成合并操作:
git commit(如果是merge冲突,Git会为你预填提交信息;如果是rebase冲突,则用git rebase --continue)。
实操心得:使用图形化工具(如VS Code内置的Git工具、GitKraken、SourceTree)解决冲突会更直观,它们会用颜色高亮显示差异,并提供点击选择保留哪边的功能,效率高很多。命令行高手则常用git mergetool命令调用配置好的比对工具。
6. 远程协作:与团队和世界同步
Git的分布式特性在远程协作中大放异彩。通常,我们会有一个中心化的远程服务器(如GitHub、GitLab、Gitee)作为大家同步代码的枢纽。
6.1 远程仓库操作
# 查看远程仓库信息(通常名为 origin) git remote -v # 添加远程仓库 git remote add origin https://github.com/username/repo.git # 从远程仓库拉取更新(fetch + merge) git pull origin main # 相当于下面两条命令: git fetch origin # 将远程最新数据下载到本地,但不合并 git merge origin/main # 将远程分支合并到当前分支 # 将本地提交推送到远程仓库 git push origin main # 如果本地分支与远程分支建立了追踪关系,可以简化 git push # 推送当前分支 git pull # 拉取当前分支的远程更新6.2 推送与拉取的最佳实践
- 推送前先拉取:在
git push之前,先执行git pull(或git fetch然后检查差异),确保本地是基于远程最新版本进行开发,避免推送冲突。 - 使用
git fetch预览变化:我习惯先用git fetch把远程更新“下载”下来,然后用git log --oneline --graph --all看看远程分支和本地分支的差异,再决定是合并还是变基,心里更有底。 - 处理推送被拒绝:如果远程有比你本地更新的提交,直接
git push会被拒绝。此时你需要先git pull合并远程更新,解决可能出现的冲突,然后再推送。如果本地历史是干净的(比如你刚做完变基),可以使用git push --force-with-lease(比git push -f更安全)强制推送,但务必确保没有其他人在这个分支上工作。
6.3 Fork & Pull Request 工作流
这是开源项目最常用的协作模式。
- Fork:在GitHub/GitLab上,将别人的项目复制一份到自己的账户下。
- Clone:将你自己账户下的仓库克隆到本地。
- 开发:在本地创建分支进行修改、提交。
- 推送:将你的分支推送到你自己Fork的仓库。
- 发起Pull Request (PR) / Merge Request (MR):在你的Fork仓库页面向原项目发起一个合并请求,请求对方审核并合并你的代码。
- 讨论与修改:在原项目的PR页面上进行代码评审、讨论。根据反馈,你可以在本地分支继续修改、提交并推送,PR会自动更新。
- 合并:项目维护者审核通过后,将你的代码合并到原项目。
这个流程完美隔离了贡献者与原项目,既保证了原仓库的安全,又为贡献者提供了标准的协作路径。
7. 高级技巧与疑难杂症排查
掌握了基础,我们来聊聊那些能极大提升效率,以及让人头疼的“坑”。
7.1 储藏(Stash):临时保存工作现场
当你正在一个分支上开发到一半,突然需要切到另一个分支去修复一个紧急bug,但当前代码又没完成不能提交。这时git stash就是救星。
# 将当前工作区和暂存区的改动储藏起来 git stash # 等价于 git stash push # 储藏时添加描述信息 git stash save "正在开发登录功能,临时保存" # 查看所有储藏栈 git stash list # 恢复最近一次的储藏,并从栈中删除它 git stash pop # 恢复指定的储藏(例如 stash@{1}),并从栈中删除 git stash pop stash@{1} # 恢复储藏,但不从栈中删除 git stash apply # 删除指定的储藏 git stash drop stash@{0} # 清空整个储藏栈 git stash cleargit stash把未提交的改动压入一个栈中,让你的工作区恢复到上一次提交的干净状态。处理完其他事情后,再用pop或apply恢复回来,无缝衔接。
7.2 后悔药进阶:重置、恢复与引用日志
git reflog:这是Git的“安全网”。它记录了本地仓库中HEAD和分支引用每一次移动的日志。即使你误操作了git reset --hard,只要提交对象还在(通常有30天以上的默认回收期),你都能通过reflog找到之前的提交哈希,然后git reset --hard commit_id恢复回来。git reflog # 找到误操作前的那个记录,例如 a1b2c3d git reset --hard a1b2c3dgit restore:这是Git 2.23版本引入的更清晰的新命令,用于替代部分git checkout和git reset的功能。# 撤销工作区的修改(等同于 git checkout -- file) git restore file.txt # 将文件从暂存区移出(等同于 git reset HEAD file) git restore --staged file.txt
7.3 子模块与忽略文件
.gitignore文件:这个文件必须放在仓库根目录。它告诉Git哪些文件或目录不需要纳入版本管理,比如编译产物(*.class,*.o)、IDE配置文件(.idea/,.vscode/)、依赖目录(node_modules/,vendor/)、系统文件(.DS_Store)等。在项目一开始就创建并配置好它,可以避免提交一堆垃圾文件。# 示例 .gitignore *.log node_modules/ .env dist/ .idea/ *.pyc __pycache__/子模块 (Submodule):用于在一个Git仓库中嵌套另一个Git仓库。比如你的项目依赖一个特定的第三方库版本。使用子模块可以将其作为只读依赖引入。
# 添加子模块 git submodule add https://github.com/other/repo.git libs/other-repo # 克隆包含子模块的仓库后,需要初始化并更新 git submodule init git submodule update注意:子模块用起来有些复杂,需要团队成员都了解其用法。现在更流行的依赖管理方式是使用语言本身的包管理器(如npm, pip, Maven)配合锁文件。
7.4 常见疑难杂症与排查
fatal: not a git repository:当前目录不是Git仓库。用git init初始化或git clone克隆一个。error: failed to push some refs:远程有本地没有的新提交。先执行git pull合并远程更新,再git push。Please commit your changes or stash them before you switch branches.:切换分支前,工作区或暂存区有未提交的修改。根据情况选择git commit,git stash或git checkout -- .(丢弃修改)。Your local changes to the following files would be overwritten by merge:合并会覆盖你本地的未提交修改。先git stash储藏起来,合并完成后再git stash pop。warning: LF will be replaced by CRLF:这是行结束符问题。Windows用CRLF,Unix/Linux用LF。通常用git config --global core.autocrlf true(Windows)或input(Mac/Linux)可以解决。- 如何修改上一次提交的信息?:
git commit --amend。这会修改最后一次提交,如果已经推送,则需要强制推送(谨慎!)。 - 如何将多个提交合并成一个?:使用交互式变基
git rebase -i HEAD~n(n为要合并的提交数),然后将后续提交的pick改为squash或fixup。
Git的学习曲线前期确实有些陡峭,但一旦你理解了它的工作模型,并形成了肌肉记忆,它就会成为你手中无比强大的工具。我的建议是,不要试图一次性记住所有命令。从最基本的clone,add,commit,push,pull开始,在真实的项目中用起来。遇到问题就去查,每解决一个实际问题,你的理解就加深一层。多用git status查看状态,多用git log --oneline --graph可视化历史,这是理清思路的最好方法。最后,记住一点:在你完全搞懂git reset --hard和git push -f在做什么之前,尽量不要使用它们。祝你在版本控制的道路上越走越顺。