写这篇文章之前我已经使用Git几年了,也看过网上的使用教程。我绝对算不上一个精通Git的人,Git有很多操作我在实际使用过程中从未用到。因此,我将通过选取一些日常开发过程中高频使用的指令和基础概念来介绍Git。
在进入正式的讲解之前,先推荐一个网站Learn Git Branching(https://learngitbranching.js.org),这个网站可以通过闯关的形式练习使用git命令,而且有直观的动画来解释每一条指令执行之后分支发生的变化。
基本概念
在学习使用Git之前,先了解几个基本概念
工作区
工作区就是我们开发的代码目录。我们从Github,Gitee,Gitlab等平台上拉取的代码,假设它的项目结构如下:
my-project/
├── src/
├── pom.xml
└── README.md这个目录就是我们的工作区,平时对代码的增删改都在工作区进行。
暂存区
暂存区可以理解为下一次提交之前的准备区。每次修改代码之后,可以使用:
git add把指定的修改放进暂存区。
然后使用:
git commit将暂存区中的内容提交到本地仓库。
本地仓库
本地仓库顾名思义就是存储在自己电脑上的代码仓库。这个仓库一般来说只有你能看见,开发团队中的其他成员是看不见的。
执行:
git commit之后,代码就会被提交到本地仓库。如果需要进一步把代码同步到远程仓库,则需要使用:
git push注:把代码提交到本地仓库这个动作不需要网络。
远程仓库
远程仓库简单理解就是开发团队共享的一个仓库,所有团队成员都能看到这个仓库里的代码(当然是在有权限的前提下)。下面有一个简单的示意图可供理解。
工作区 Working Tree
│
│ git add
▼
暂存区 Staging Area
│
│ git commit
▼
本地仓库 Local Repository
│
│ git push
▼
远程仓库 Remote Repositorygit add
git add是一条非常基础的指令,常用IDEA图形化界面的没怎么用过这条指令。假设我修改一个文件test.java,在使用add指令之前,我所做的修改是不会被git记录的,使用:
git add test.java之后,修改会被添加到暂存区。也可以使用:
git add .这会把当前所有的修改都放进暂存区。
这条指令并不是提交代码,只是在告诉Git:这些修改准备在下一次的commit中提交。
git commit
git commit用于提交代码,在做了一系列修改之后,我们需要把这些修改保存到本地仓库,后续继续使用push命令将代码从本地仓库同步到远程仓库。这条指令只会提交暂存区中的内容。
例如:
例如:
修改 A
修改 B
修改 C如果只执行:
git add A
git add B然后:
git commit -m "提交 A 和 B"那么 C 并不会被这次 commit 提交。
git pull
pull顾名思义是“拉”,这条指令用于把代码从远程仓库拉取到本地仓库,同时会将远程的更新合并到当前分支,可以理解为git pull = git fetch + git merge。注意,首次拉取代码一般使用git clone指令,后续如果要更新自己本地仓库中的代码,使之与远程仓库保持同步,就要使用这条指令。一个比较常见的开发习惯是,在开始新的功能开发之前,先确认当前分支已经同步了远程仓库的最新代码,在push代码之前同样也确认一下。
git fetch
git fetch和git pull经常一起出现,老实说我在写这篇文章之前也不知道具体的差别,因为我总是使用IDEA中的图形化界面,然后点击update按钮对代码进行更新。
它们最大的区别可以简单理解为:
fetch只负责获取远程更新,pull获取更新之后还会进行整合。
例如:
git fetch origin执行之后,Git 会获取远程仓库的新提交,但不会自动把这些提交合并到当前分支。
git push
与pull相对应的命令就是push,顾名思义就是把代码“推”到远程仓库。在进行push操作之前,最好更新一下本地仓库的代码。
git branch
git branch 用于查看和管理 Git 分支。
直接执行:
git branch可以查看当前本地有哪些分支。
例如:
* main
develop
feature/login这里的 * 表示当前所在的分支。
如果只是想查看当前到底在哪个分支,也可以使用:
git branch --show-currentgit reset
git reset 是相对复杂的一条命令。
我自己比较常用的一个场景是:
commit 一次之后发现代码还需要修改,而且这个 commit 还没有 push 到远程仓库,希望把这次提交重新整理一下。
例如:
git commit -m "完成登录功能"提交之后发现还有一点代码需要修改。
这时候可以通过 reset 将当前分支回退到之前的状态,然后重新整理修改并 commit。
Git reset 常见的三种模式是:
--soft
--mixed
--hard可以简单理解为:
| 模式 | HEAD | 暂存区 | 工作区 |
|---|---|---|---|
--soft | 回退 | 保留 | 保留 |
--mixed | 回退 | 重置 | 保留 |
--hard | 回退 | 重置 | 重置 |
其中:
git reset --mixed也是 git reset 默认使用的模式。在这个模式下,commit会被撤销,暂存区也会被重置,但是工作区的代码修改会保留,可以在此基础上继续修改代码然后重新commit。
git stash
git stash是我觉得非常实用的一条指令,而且我觉得这条指令在IDEA的界面里反而不容易找到,有必要学一下。当前分支还有一些没有 commit 的修改,但是突然需要切换到其他分支处理事情可以使用这条指令。在切换分支之前如果还有未commit的修改想要保存,使用git stash存,切回分支之后使用git stash pop将暂存的修改从栈中弹出来。
实例
前面分别介绍了几个命令,但实际开发的时候,我们通常不会单独使用它们,而是组合起来使用。
假设现在公司有一个 Java 项目:
main今天需要开发一个登录功能。
首先切换到 main:
git switch main然后获取远程最新代码:
git pull接着创建自己的功能分支:
git switch -c feature/login然后开始写代码:
LoginController.java
LoginService.java
LoginMapper.java代码写完之后查看修改:
git status然后将修改加入暂存区:
git add .提交:
git commit -m "新增用户登录功能"此时:
工作区
↓ add
暂存区
↓ commit
本地仓库代码已经提交到了自己的本地 Git 仓库。
接下来将代码推送到远程仓库:
git push origin feature/login此时:
本地仓库
↓ push
远程仓库如果在开发过程中突然需要切换到其他分支,而当前还有没有提交的修改:
git stash处理完其他事情之后:
git switch feature/login
git stash pop如果提交之后又发现这次 commit 需要重新整理,并且还没有 push 到远程仓库,则可以考虑使用:
git reset如果只是想先看看远程仓库最近有什么变化,而暂时不想合并:
git fetch而如果希望直接把远程更新同步到当前分支:
git pull所以日常开发中,一个比较典型的流程就是:
git pull
↓
创建 feature 分支
↓
修改代码
↓
git add
↓
git commit
↓
git push
↓
远程仓库 / PR图形化界面
其实完全没必要熟知每一条指令,使用IDEA或者Vscode都有对应的图形化界面,操作起来甚至更简单。不过命令行最大的优势之一就是操作非常明确,而且可以通过命令历史回溯自己之前做过什么操作。而在图形化界面中,某些操作可能只是点击了几个按钮,过一段时间之后自己甚至都不知道当时到底执行了什么 Git 操作。
总结
git是一个强大的版本管理工具,有各种各样的指令。对于日常开发来说,与其一开始死记硬背几十甚至上百条 Git 命令,不如先掌握这些高频操作。在实际开发过程中遇到问题,再去了解新的命令。
如果想进一步学习 Git,也推荐多使用一些交互式的练习工具,通过实际操作理解分支、commit、HEAD 等概念。