
Git版本控制
更新: 2025/2/24 字数: 0 字 时长: 0 分钟
介绍
版本控制,就是对软件开发过程中的各种程序代码、配置文件、说明文档等文件的变更进行管理。想象一个场景,领导让你写一个项目方案,当你写好后,领导觉得不行,让你修改,于是你改出了第二稿、第三稿、第四稿......,最终领导说:“还是用第一版吧”,于是你攥紧了拳头回到工位上,拿出了第一版的方案。在整事件中,将文件版本快速回退到特定版,这就是一个版本控制的例子。
一个软件从开发到上线往往由多人共同完成,中间会经历多个版本的迭代,每次迭代都会修改代码和文件,因此必须使用版本控制工具把什么人、什么时候、更改了什么全部记录下来,方便日后查看和恢复。有了版本控制工具,团队中如果有人完成了一个功能,其他人就可以通过拉取代码来共享开发成果;如果有人不小心写了 Bug 造成新版本无法运行,其他人也可以随时将代码回退至可以正常运行的历史版本。常用的版本控制工具分为如下三类:
点击查看
本地版本控制:将所有版本放在本地管理,每次更新内容,都会记录一个版本文件,需要哪一个版本就去找对应的版本文件,不涉及联网操作,适合个人使用,上面场景假设中改方案的例子就属于本地版本控制。
集中式版本控制:所有的版本数据都放在服务器上管理,也只有服务器上才有完整的版本数据,团队成员可以从服务器上拉取代码或将本地的代码上传。
分布式版本控制:每个团队成员的电脑上都有一个完整的版本控制中心,从服务器上拉取代码后,本地也就有了所有代码数据,这种方式既能本地开发,也能离线提交。如果网络出现问题,也可以在联网之后,再将代码推送到服务器上。就算服务器宕机,也不会影响开发进度,哪怕服务器损坏,也不会造成数据丢失。
不同的版本控制系统各有优缺点,比如集中式版本控制,在服务器宕机或断网的情况下,成员的本地代码就无法更新,也无法访问历史版本,但因其便捷的统一管理和较好的保密性,目前依然会有公司使用集中式版本控制系统,其中最具代表性的产品就是 SVN。还比如分布式版本控制,由于每个人都可以拿到全部的开发代码,所以会存在一定的泄密风险,但因其支持脱机工作和更高的协作效率,成为了绝大部分公司选择的对象,其中最具代表性的产品就是 Git。Git 是诞生于 2005 年的一个免费且开源的分布式版本控制系统,因其强大的功能和便捷的操作命令,逐渐发展为软件开发中必不可少的工具。可以说,会用 Git 是一个程序员的必备技能。
提醒
Git 的开发初衷是林纳斯·托瓦兹(Linus Torvalds)为了帮助管理 Linux 内核开发,因此 Git 的一些操作方式与 Linux 命令行风格相似,毕竟它们都是由林纳斯开发的。
下载安装
首先下载 Git 版本控制工具,步骤如下:
点击查看
访问 Git 的官网地址 https://git-scm.com/ ,首页显示了当前 Git 的最新版本号,点击
Download for Windows
按钮跳转到下载页面。下载页面提供了安装文件的下载链接,根据你电脑的操作系统来选择下载 32 位的文件,还是 64 位的文件。
提醒
不知道电脑操作系统的位数,请参看《如何查看电脑是32位还是64位》
然后安装 Git 版本控制工具,步骤如下:
点击查看
双击打开下载的安装程序,点击
Next
按钮。选择 Git 的安装位置,不建议放在 C 盘(系统盘)。
进入 Git 选项配置,用默认勾选如图的 4 个部分。
- 第 1 部分是将 Git 的命令行客户端(Git Bash)和 GUI 图形界面客户端(Git GUI)关联到右键菜单。
- 第 2 部分是支持大文件。
- 第 3 部分是关联配置文件的默认编辑器。
- 第 4 部分是将
.sh
脚本文件和 Bash 关联。
这一步我们继续点击
Next
按钮。选择 Git 的默认编辑器,默认选择 Vim 编辑器。
选择新仓库的初始化分支名称,选择
Let Git decide
则默认分支名就都是master
,如果不想使用master
作为初始化分支名称,就选择下面一项,并在输入框输入你自定义的名称,我们继续点击Next
按钮。添加环境变量,我们按默认选择第二项,即添加完环境变量之后,不仅可以在 Git Bash 中使用 Git 命令,还可以命令行窗口中使用 Git 命令。
选择第一项 SSH 的可执行文件。
选择 HTTPS 连接协议,选择默认的 OpenSSL 选项。
配置 Git 行末换行符,在 Windows 中的行末换行符是默认的
CRLF
和\r\n
回车换行一样,而 Linux 默认的行末换行符是LF
和\n
换行一样,这里我们选择第一项,Git 自动检查 Windows 的行末换行符,以 Linux 方式提交。选择 Git Bash 使用的终端模拟器,第一个是 MinTTY 终端,第二个是 Windows 终端,默认选择第一个。
选择
git pull
的默认行为,git pull
是从服务器往本地拉取代码的命令,第一项是执行git pull
命令时,如果没有冲突,则自动将当前分支更新到最新,如果存在冲突,则发起merge
请求。这里我们默认选择第一项。这里选择第一项跨平台凭证管理器,我们在使用 Git 上传或下载代码时,需要登录账号,凭证管理器就是管理登录的账号信息。有了凭证管理器,我们在同一台电脑上只需登录一次,之后再使用 Git 就不用重登了。
其他配置,第一项是是否使用文件缓存机制,使用缓存 Git 会更快一些,第二项是是否支持软链接文件,可以不选。
实验性配置,这里是新版本发布的两个实验性配置选项,可以存在 Bug 所以不选,点击
Install
进行安装。完成安装后,最后点击
Finish
按钮结束安装。
提醒
SSH 是一种网络协议,用于计算机之间的传输文件或远程控制。OpenSSH 提供了程序和客户端工具,对传输的文件数据进行加密处理。
最后验证 Git 版本控制工具,步骤如下:
点击查看
在桌面右键单击鼠标,在下拉菜单中我们点击 Git Bash Here 进入客户端。
输入命令
git --version
出现版本信息就说明安装成功了。
建议
虽然使用 Git 的图形界面客户端(Git GUI)会更加方便,但推荐使用 Git 的命令行客户端(Git Bash),因为并不是每台电脑都有可视化界面供你使用,这也是我们学习 Git 命令的意义。
工作流程
在《文档03-GitHub托管平台》中介绍了全球最大、最知名的代码托管平台 GitHub,在国内也有一些代码托管平台,比较知名的有 Coding、码云。不过公司出于对自身商业代码保护的原因,一般都不会使用这些公共代码托管平台,即使它们提供了私有仓库服务,公司依然会选择在局域网内搭建私有代码托管平台(Git 私服)来存储和管理自身的商业代码,其中最具代表性的产品就是 GitLab,相关介绍如下:
点击查看
- 产品功能:GitLab 提供了版本控制、缺陷管理、持续集成等一系列强大的功能,而且使用界面和 GitHub 也比较像。
- 产品版本:GitLab 有 CE(Community Edition)免费版和 EE(Enterprise Edition)付费版两种,如果是个人开发者或小型团队使用的话,CE 版本功能足够满足基本需求;如果需要企业级功能(如高级安全性、代码质量分析、权限管理等),可以选择 EE 版本。
- 产品搭建:搭建 GitLab 对机器的要求也比较高,而且启动也比较慢,要搭建的话,建议使用 Docker 来搭,具体这里不细讲,等后面学习 Linux 操作系统的时候,再来介绍如何搭建和使用。
建议
如果你是个人开发者,而且不会正确上网的话,使用 GitHub 会受到网络限制,因为它的服务器在国外,速度有时候会有点慢。想要速度快的话,建议使用国内的码云。
有了公司的私有代码托管平台后,程序员可以将自己的开发成果提交到平台上,便于代码的集中管理和团队协作,同时也为代码的备份和版本控制提供了保障。回到程序员的开发工作类型,大体可以分为以下两类:
点击查看
单人独立开发:
- 在公司的代码托管平台上创建一个远程仓库。
- 将远程仓库克隆到本地,生成本地仓库并开始开发。
- 每完成一个小功能,进行版本提交,并将代码推送到远程仓库。
- 测试人员可以直接从远程仓库拉取代码进行测试,确保开发成果的稳定性。
多人协作开发:
- 由负责人创建远程仓库并初始化项目。
- 每位开发成员从远程仓库克隆一份副本到本地,并在各自的分支上进行开发。
- 完成一个功能模块后,查看并提交修改内容。
- 如果远程仓库已有其他成员的新提交,需要先将远程最新内容拉取并合并到本地。
- 处理冲突(如果有)后,再将本地修改推送到远程仓库,并完成合并。
无论是单人独立开发,还是多人协作开发,只要使用了 Git 就会涉及到以下概念:
点击查看
- 本地仓库:由于本地计算机有着便捷的编辑器和代码调试环境,因此绝大部分开发工作都是在本地进行的。这时就必须对项目进行版本控制,当项目目录进行了 Git 初始化或从远程仓库克隆了项目代码之后,该项目目录就变成了一个本地仓库。
- 工作区:进行项目开发工作的区域,包括文件的编辑、修改、删除都在工作区完成。
- 暂存区:存放文件快照的区域,这些快照可以被修改或移除。在文件有快照的情况下,如果在工作区不慎误删了某段代码或文件,就可以通过文件快照进行还原,降低代码的遗失风险。
- 版本库:存储所有提交记录和版本历史的区域,在暂存区提交文件快照后,会自动生成一个唯一的版本号(提交 ID),后续可以随时通过版本库中的版本号将代码回溯。
- 远程仓库:托管在网络中的项目版本库,也就是线上的代码托管平台,例如 Github、GitLab、Gitee 或公司内部的代码托管平台,只要有权限,就可以访问、拉取和修改里面的代码。在确定代码没问题之后,我们就可以将版本库中最新的修改记录推送到远程仓库上了。
了解上述的开发工作和基础概念后,整个提交代码的流程就可以分为如下三步:
点击查看
将工作区的修改提交到暂存区进行保存,可以多次修改多次提交。
将暂存区累积的修改进行封版并提交到版本库,工作区就可以通过版本号进行历史回溯。
将版本库的版本修改推送至线上的远程仓库。
注意
版本提交之后,你的代码和版本记录只有本地仓库才有。如果你不小心写入隐私信息,此时你只要把完整的代码复制出来,将本地仓库删除,再重新初始化一个 Git 目录,也就是创建一个新的仓库,把修改后的代码放进工作区,再作为第一个版本提交即可。但如果你已经将代码推到了远程服务器上,进入了远程仓库,这就比较危险了,相当于把你的隐私直接公布给了大家。
本地管理
本地管理将会围绕本地仓库,从初始使用配置、提交改动封版以及忽略丢弃回退这三个部分展开讲解。
初始使用配置
初始使用配置主要介绍初始化 Git 本地仓库以及使用相关的配置。
初始本地仓库
我们右键打开 Git Bash,执行下面命令创建并初始化一个本地仓库:
# 创建test目录
mkdir test
# 进入test目录
cd test
# 将test目录初始化为本地仓库
git init
在本地仓库内有一个 .git
隐藏目录,这个目录是 Git 的核心数据存储区,里面存储了所有与版本控制相关的信息,因此不要去改动里面的文件,否则可能会导致仓库失效。简单了解一下 .git
目录中所包含的内容:
- 暂存区,位于
.git/index
文件中,用于保存当前暂存的文件快照。 - 版本库,位于
.git/objects
目录中,存储了所有提交的版本记录。 - 配置信息,位于
.git/config
文件中,用于存储仓库的配置信息。 - 分支信息,位于
.git/refs/heads
目录中,用于记录分支指针。
提醒
将一个空目录初始化为 Git 本地仓库后再添加文件,与直接将一个已有文件的目录初始化为 Git 本地仓库,两者没有差别。另外,在上面讲到的工作区,也就是我们日常操作文件的区域,所对应的是 .git
目录同级路径及其子路径下的所有文件和目录。
重要
值得注意一下的是,部分 Git 命令(如 git status
和 git add
)的作用范围仅限于当前目录及其子目录中的文件,而其他一些命令(如 git commit
和 git log
)则会作用于整个仓库。因此,为了避免因目录层级导致的作用范围限制或不一致,最好在 .git
目录的同级路径(仓库根目录)下执行 Git 命令,以确保所有操作都能准确覆盖整个仓库。这是 Git 操作中的一个重要原则,需要牢记。
用户身份配置
配置用户信息是使用 Git 的必要一步,其目的是为了追溯提交代码的作者是谁,不然是无法提交修改记录的。好比考试,不能交一份没有写名字的卷子。而且为了能够使用不同的身份来提交代码,或者同时访问多个 Git 仓库并使用不同的身份进行认证,Git 提供了配置多个用户信息的功能,所以 Git 用户配置就可以分为如下两类:
局部用户配置(在仓库根目录下执行):指的是为特定的仓库设置用户名和邮箱地址,这些设置仅在该仓库内有效,通常用于区分不同项目或不同环境下的身份信息。如果你需要在其他仓库中使用不同的用户信息,你需要为每个仓库分别进行配置。
bash# 设置局部用户名 git config --local user.name <用户名> # 设置局部用户邮箱 git config --local user.email <邮箱>
全局用户配置(在任何路径下执行):默认用户信息会应用于所有的仓库,但如果局部配置存在,Git 将优先使用局部配置的信息。
bash# 设置全局用户名 git config --global user.name <用户名> # 设置全局用户邮箱 git config --global user.email <邮箱>
通过下面命令可以查看、修改、删除配置的用户信息,需要注意的一点是命令的执行路径,全局用户信息配置命令在任何路径下执行均可,但局部用户信息配置命令只能在 Git 仓库根目录下执行:
# 查看全局/局部的配置信息
git config --[global/local] -l
# 查询全局/局部的某项配置信息
git config --[global/local] <键>
# 修改全局/局部的某项配置信息
git config --[global/local] <键> <值>
# 增加全局/局部的某项配置信息
git config --[global/local] --add <键> <值>
# 删除全局/局部的某项配置信息
git config --[global/local] --unset <键>
中文编码配置
默认情况下,Git 在输出文件路径时会对非 ASCII 字符(如中文)进行编码显示,例如使用 git status
命令查看文件状态时会将含有中文字符的文件名显示为类似 \346\200\241\345\210\206.txt
的形式,其原因是由于 Git 的 core.quotepath
配置项的默认值为 true
所造成的。执行下面的命令将 core.quotepath
设置为 false
,设置后,Git 会按照操作系统的字符编码(通常是 UTF-8)显示文件名,这时中文就能够正常显示。
git config --global core.quotepath false
建议
如果你经常处理中文文件名,建议执行该命令全局设置 core.quotepath
为 false
。备注:该设置不会影响文件名的实际存储,仅影响输出的显示方式。
提交改动封版
提交改动封版主要介绍提交代码进行封版的流程,也是最常用的一套工作流。
查看仓库状态
首先,我们学习第一个重要的命令就是查看本地仓库状态:
# 查看本地仓库状态
git status
On branch master
提示当前正处于master
分支,这也就是上面我们安装 Git 时,选择的 Git 默认初始化分支名称。No commits yet
提示当前仓库还没有任何版本提交,是一个空仓库。nothing to commit (create/copy files and use "git add" to track)
提示当前库里没有可提交的内容,可以创建或拷贝文件进来,通过git add
命令进行跟踪。
现在我们在本地仓库里面新增一个 1.txt
文件,执行 git status
命令,结果中有一行 Untracked files
提示工作区中有未被跟踪的文件,在下方 红色文件名
表示工作区未被跟踪的文件:
提交工作改动
如果想建立对 1.txt
文件的跟踪,需要执行 git add
命令将工作区的改动(包括文件的新增、改动、删除)提交到暂存区:
# 仅提交指定文件的改动到暂存区
git add 路径/文件名称
# 提交当前目录及其子目录下所有文件的改动到暂存区
git add .
提醒
执行 git add
命令将工作区的改动提交到暂存区后,如果工作区又有了新的改动(比如,修改了1.txt
文件、新增了 2.txt
文件),就需要再次执行 git add
命令,将工作区的最新改动提交到暂存区。
在执行了 git add .
命令后,使用 git status
查看本地仓库的状态,结果返回 new file 绿色文件名
,这表明了 1.txt
文件被成功添加到了暂存区并建立了跟踪:
提交暂存定版
接下来如果想将暂存区的内容固定为一个版本,就需要执行 git commit
命令将暂存区的改动提交到版本库进行封版,命令说明如下:
# 将暂存区的改动提交到版本库
git commit -m '关于此次提交内容的简短描述'
git commit -m '提交1.txt文件'
将暂存区的改动提交到版本库,本次的提交描述为提交1.txt文件
。[master f438911] 提交1.txt文件
提示master
分支上有了新的版本,f438911
是版本号的前七位(完整的版本号有 40 位),提交1.txt文件
就是该版本的描述信息。1 file changed, 0 insertion(+), 0 deletions(-)
提示新版本中一个文件有修改,内容既没有增加一行,也没有减少一行。git status
查看仓库状态,nothing to commit, working directory clean
提示工作区、暂存区干净,没有需要提交的内容。
提醒
执行 git commit
命令时,Git 会使用 SHA-1 哈希算法对当前提交的内容(包括文件内容、目录结构、父提交信息、提交者信息、时间戳等元数据)进行一次运算,生成一串长度为 40 个字符的十六进制哈希值,这就是 commit ID
版本号的由来。这个版本号是一个庞大的数字,包含了足够的位数,确保了在多人多次的协作开发过程中一个版本号对应一个版本。
忽略丢弃回退
忽略丢弃回退主要介绍在提交封版的过程中需要忽略、丢弃和回退的情况。
忽略文件目录
在初始化本地仓库后,Git 默认会将仓库中所有的文件和目录都纳入跟踪,但是有些文件(例如,日志文件、临时文件、编译产生的中间文件等)是无需跟踪的。对于这种情况,就需要手动创建一个名为 .gitignore
的版本控制忽略文件(注意文件名称是默认的不能变动,它是以 .
为开头且没有后缀),在里面列出要忽略的文件或目录,Git 会根据内容判断是否将文件或目录添加到版本控制中。具体 .gitignore
文件内容格式规范如下:
点击查看
- 所有空行或者以注释符号
#
开头的行都会被 Git 忽略。 - 以斜杠
/
结尾的说明要忽略目录以及目录下的所有内容。 - 要忽略指定范围以外的文件或目录,需要在加上感叹号
!
取反。 - 使用正则表达式进行匹配,规则如下:
*
匹配零个或多个任意字符。?
问号匹配一个任意字符。[]
匹配方括号中任意一个字符,如[ab]
表示匹配a
或者匹配b
。[n-m]
匹配在两个字符范围内的所有字符,如[0-9]
表示匹配0
到9
的所有数字。
- 例举
.gitignore
文件,内容如下:
# 忽略当前路径下的logs目录,包含logs下的所有子目录和文件
logs/
# 忽略根目录下的logs.txt文件
/logs.txt
# 忽略所有后缀为.class的文件
*.class
# 忽略tmp目录后缀为的.txt文件
tmp/*.txt
# 不忽略classes目录下的a.class文件
!/classes/a.class
在例举的 .gitignore
文件中,只添加了当前项目中需要忽略的文件和目录,但如果是在商业项目中,还要添加许多内容,比如说系统产生的临时文件、缓存文件等,这些文件都不用纳入版本控制,而且写起来也比较麻烦,所以这里借助 生成版本控制忽略 网站来自动生成版本控制忽略清单,使用流程如下:
点击查看
点击链接 https://gitignore.io,访问 gitignore 网站。
根据输入框中的提示信息进行输入,比如项目中使用了 Python 语言,使用了 PyCharm 工具开发,使用的系统包括 Windows、Linux、macOS,然后点击创建按钮。
页面跳转到网站自动生成的版本控制忽略清单(下面图中只展示了部分内容,实际有较长一段),将里面的所有内容复制粘贴到项目中的
.gitignore
文件中后,最后再手动加上项目中常规忽略的虚拟环境目录(例如env
、venv
)以及自定义忽略的文件即可。
**需要注意的是,.gitignore
文件只能忽略未被 Git 跟踪的文件或目录。如果某个文件或目录已经被添加到暂存区或提交到仓库中,即使之后将其路径添加到 .gitignore
文件中,它仍然会被 Git 跟踪和显示。**如果希望忽略这些已经被跟踪的文件或目录,可以执行下面命令将后来添加到 .gitignore
文件中的文件或目录从 Git 版本控制中移除:
# 移除指定文件的跟踪
git rm --cached <文件名>
# 移除指定目录的跟踪
git rm --cached -r <目录路径>
# 递归移除所有文件和目录的跟踪
git rm --cached -r .
移除文件目录
在本地仓库中新建的文件和目录只要没被添加到暂存区,则都是处于未被跟踪的状态。如果想从本地仓库中移除未被跟踪的文件和目录,可以使用以下命令:
# 列出将要被删除的未被跟踪文件
git clean -n
# 列出将要被删除的未被跟踪目录
git clean -nd
# 交互式删除(逐步选择未被跟踪的文件或目录)
git clean -i
# 强制删除未被跟踪的单个文件(若要删除所有未被跟踪的文件,不加文件名)
git clean -f <文件名>
# 强制删除未被跟踪的单个目录及其内部文件(若要删除所有未被跟踪的目录及其内部文件,不加目录名)
git clean -fd <目录路径>
注意
需要注意的是,git clean
命令是从磁盘上永久删除文件和目录,属于不可逆操作,请谨慎使用!另外,如果在执行 git clean
命令删除文件和目录时没有加上 -f
选项,会出现 fatal: clean.requireForce defaults to true and neither -i, -n, nor -f given; refusing to clean
错误。这是因为默认情况下,Git 要求使用 -f
选项来确认你要强行执行 git clean
命令,以避免意外删除文件。
丢弃工作改动
上面提到执行 git add
命令后会将未跟踪文件提交到暂存区并建立对该文件的跟踪,现在如果对被跟踪的 1.txt
文件执行一些操作,执行 git status
命令看看返回的结果有哪些不同:
如果对
1.txt
文件的内容进行修改,执行git status
命令,结果返回modified 红色文件名
,表明被跟踪文件在工作区有内容修改。如果删除
1.txt
文件,执行git status
命令,结果返回deleted 红色文件名
,表明被跟踪文件在工作区被删除。
如果这时你突然发现对 1.txt
文件的修改、删除是误操作,可以使用下面的命令丢弃被跟踪文件在工作区中的改动,具体命令如下:
# 丢弃指定已跟踪文件在工作区的改动
git restore 路径/文件名称
# 丢弃所有已跟踪文件在工作区的改动
git restore .
提醒
如果你的 Git 版本较低,并且没有 git restore
命令的支持,可以使用 git checkout .
来丢弃被跟踪文件在工作区的修改。
注意
注意 git restore
命令针对的是工作区的被跟踪文件,对于工作区的未跟踪文件以及暂存区中的内容是不起作用的。另外,git restore
命令的丢弃操作是不可逆的,请谨慎操作!
撤回暂存提交
上面提到执行 git add
命令后会将未跟踪文件提交到暂存区并建立对该文件的跟踪,如果想撤回提交到暂存区的改动,但保留文件在工作区中的改动,可以按需求执行下面的命令:
# 撤回指定文件在暂存区的改动,保留其在工作区的改动,并移除其跟踪状态
git rm --cached <文件名>
# 撤回指定目录在暂存区的改动,保留其在工作区的改动,并移除其跟踪状态
git rm --cached -r <目录路径>
# 撤回指定文件在暂存区的改动,保留其在工作区的改动,不改变其跟踪状态
git reset 路径/文件名称
# 撤回所有文件在暂存区的改动,保留其在工作区的改动,不改变其跟踪状态
git reset .
回退历史版本
在上面的操作中,我们已经提交了一个版本,通过下面命令可以查看已提交的历史版本信息:
# 查看版本库信息(显示当前分支完整ID、提交作者、提交时间、提交说明)
git log
# 查看版本库简略信息(显示当前分支部分ID、提交说明)
git log --oneline
commit f438911...
当中f438911...
这 40 字符就是完整的commit ID
版本号,这个和上面git commit
提交后输出的版本号是一样的,只不过这个是完整的版本号,而上面的commit
只显示了前七位。Author: chenzhuo [email protected]
是提交的作者信息,就是上面配置仓库用户的user.name
和user.email
信息。Date: Sun Nov ...
是版本提交的时间。提交1.txt文件
是提交版本的描述。
现在我们继续进行修改并提交一些版本,假如在此过程中提交错误的修改并进行了定版,想回退到之前正常的版本,可以执行 git reset
命令,具体参数效果如下:
以上参数中,常用的就是 --hard
参数,它能让工作区、暂存区、版本库全部回退到指定版本,具体的使用方式如下:
# 舍弃工作区和暂存区的所有修改,重置为最新提交状态
git reset --hard
# 回滚到上一个版本
git reset --hard HEAD^
# 回滚到上上一个版本
git reset --hard HEAD^^
# 回滚到上上上一个版本
git reset --hard HEAD~3
# 回滚到指定版本(ID号就是commitID的前七位,如果不唯一,就必须使用完整的commitID)
git reset --hard <ID号>
现在我们实际操作一下如何进行版本回退,具体操作如下:
点击查看
执行
git reflog
命令查看所有的版本,确定要回退到的版本。bash# 查看当前分支的所有操作记录(简单、快速的查看版本库情况,包括已经被删除的commit和reset的操作) git reflog # 查看所有分支的所有操作记录(简单、快速的查看版本库情况,包括已经被删除的commit和reset的操作) git reflog --all
172e41b
是版本号的前七位。HEAD -> master
是 HEAD 指针指向该版本的master
分支,表明当前工作区的代码是该版本的master
分支上的内容。commit: version 6
是版本在提交时的描述信息。
执行
git reset --hard
命令,经过几次版本回退,最终将版本重置到了 version 2。假如觉得当前回退到 version 2 版本太老旧,想去到未来的 version 4 版本,仍然是执行
git reflog
命令获取所有版本,复制 version 4 版本的版本号,然后执行git reset --hard <ID号>
命令,最终将版本重置到 version 4。
重要
这里有一点需要记住的是,git log
命令只能查看当前版本及其之前提交过的历史版本,git reflog
命令可以查看所有的提交过的历史版本。打个比方,仓库中一共有六个版本,现在回退到了版本三,如果执行 git log
命令只能查看到版本一、版本二以及当前版本三的信息,但如果执行 git reflog
命令就能查看到全部六个版本的信息。
警告
前面我们说,假如你不小心写入了隐私信息,并且还完成了提交,那就只能删掉整个本地仓库了。这是因为你所有的提交版本,都会记录在本地仓库中,且不会消失,哪怕你进行了版本回退,提交记录依然可以恢复,所以只有删库。当然删库是不可取行为,所以提交代码时,一定要认真谨慎。
分支管理
分支管理是版本控制系统中用于创建、切换和合并**不同开发线路(分支)**的关键功能,旨在支持团队并行开展新功能开发、问题修复、版本发布等工作。
分支机制优势
试想在没有分支机制的情况下,多个开发者同时编辑一个项目会面临哪些问题?例举如下:
- 覆盖他人更改:如果一个开发者先修改了代码,而另一个开发者在不知情的情况下修改了相同的文件,可能会覆盖前者的更改,导致功能丢失或修复失效。
- 协作效率低下:开发者无法在独立的环境中进行新功能开发,往往需要等待其他人的修改,导致开发进度滞后。例如,开发者 A 在进行大功能开发时,开发者 B 可能不得不等待或者修改代码,这可能会干扰 A 的工作。
- 代码质量问题:多个开发者同时提交代码可能导致质量问题没有及时发现,进而影响到其他开发者的工作进度和系统稳定性。
可以预见,在缺乏分支管理的情况下,团队协作往往会面临冲突、代码丢失、集成困难等问题,这些问题会严重影响项目的稳定性和开发效率。因此,合理的分支管理在团队协作开发中至关重要。它不仅能够显著提高团队成员的开发效率,还能有效避免多人在同一环境中进行开发和发布时可能引发的冲突与混乱,确保代码质量,进而使代码管理更加规范和有序。此外,分支管理还可以在发布前进行多轮测试,确保只有经过验证的稳定版本最终合并到主分支。这就像餐厅厨房的工作分配:有人切菜、有人切肉、有人调味,最后将所有食材合并进锅中翻炒,做成一道美味的菜肴,最终端上桌。这正是从开发到发布的过程。
关系特点说明
首先,说明一下什么是主分支?什么是其他分支?主分支是项目的默认分支,通常是项目的起始分支,代表项目的稳定版本或生产版本。而其他分支则是从主分支衍生出来的独立副本,用于并行开发不同的功能、修复问题或进行实验性工作。此外,所有分支还具有以下三个主要特点:
- 灵活性:分支的创建非常灵活,不仅可以在主分支上创建新分支,还可以在任何已有分支上创建子分支。
- 集成性:通过合并操作,可以将不同分支的修改整合到一个分支中。
- 独立性:各个分支之间(包括主分支)是相互独立的,因此,分支的创建、修改或删除不会直接影响其他分支。
命名含义作用
在项目开发中,项目成员会基于同一个项目创建独立的分支,因此分支的命名就必须遵循一定的规范,通常为如下形式:
分支名称 | 含义 | 作用 |
---|---|---|
master/main | 主分支 | 用于保持项目的稳定版本,代表已发布的生产版本。 |
release | 预发布分支 | 用于准备发布版本的分支,通常用于最后的调试、文档编写等工作。 |
feature | 功能分支 | 用于开发新功能,每个功能都会有一个独立的分支。 |
bugfix | 修复分支 | 用于修复在开发过程中发现的某个特定的 bug,通常基于 develop 或 main 分支。 |
hotfix | 热修复分支 | 用于快速修复生产环境中的紧急问题,通常会从 main 分支派生。 |
dev/develop | 开发汇总分支 | 用于汇总所有人的开发工作,通常是团队开发的主要分支。 |
dev_name | 个人开发分支 | 用于个人的开发工作,通常是为每个开发者单独创建的分支。 |
提醒
Git 的标准使用流程(分支管理策略)一共有三套,分别是 git-flow、github-flow、gitlab-flow,因篇幅过长不细讲,自行了解即可。
重要
一般情况下,主分支名称基本都为 master
,这个是在上面安装 Git 第 6 步设置新仓库默认初始化分支名称时确定的。因此在非必要情况下,不要使用其他的初始化分支名称。另外,不管使用哪种一种分支管理策略,master
分支都是受保护的分支,普通开发者不能直接去改动 master
分支的,也不能将自己的开发成果直接放到 master
分支。
分支相关操作
查看活动分支
首先,执行下面的命令查看当前仓库中的所有分支以及所在的活动分支:
# 查看当前仓库中的所有分支,其中*号标记的就是当前所在的活动分支。
git branch
提醒
在 Git Bash 命令行终端提示符末尾会显示当前所在的本地分支,图中显示(master)
就表示当前所在的活动分支是 master
分支。如果使用其他的命令行工具,就不会显示当前的活动分支,这时需要执行 git branch
命令,通过 *
标记确定当前所在的活动分支。
需要说明的是,执行 git init
初始化本地仓库后,虽然初始化分支 master
已经存在,但因为没有 commit
提交记录(空历史),此时 master
分支处于尚未激活的状态,因此执行 git branch
不会返回任何分支信息。只有在执行一次 git commit
后,master
分支才会正式激活,此时再次执行 git branch
就会显示 master
分支。
修改分支名称
假如想修改分支名称,可以执行下面命令(需要注意,分支的新名称不可和已有分支名称重复,否则重命名会失败):
# 分支改名
git branch -m <分支旧名称> <分支新名称>
创建新的分支
假如想创建一个新分支,可以执行下面命令(需要注意,新建分支名称不可和已有分支名称重复,否则创建分支会失败):
# 创建新分支(未指定分支的情况下,会基于当前分支创建新分支)
git branch <新建分支名称> <可选:指定分支>
这里同上面一样,Git 本地仓库初始化后,在没有任何的 commit
操作之前,Git 是无法创建新分支的,因为没有提交历史可供分支依赖,因此需要至少进行一次 commit
操作封版生成主分支之后,才可以在主分支上创建新分支。
提醒
因为新分支的内容是从指定分支的当前状态复制过来的,所以新建分支和指定分支具有相同的历史和内容,除非新分支或指定分支有新的提交。
切换活动分支
上面在指定的 master
分支上新建了 new
分支,但 *
号标记还在 master
分支,说明当前依然处于 master
分支上,如果要切换到上面新建的 new
分支可以执行下面的命令:
# 切换分支(推荐)
git switch <其他分支名称>
# 切换分支(不推荐)
git checkout <其他分支名称>
上面返回 Switched to branch ...
提示,说明分支切换成功。如果觉得新建分支再切换分支太麻烦,可以使用下面的命令:
# 新建分支并切换到新建的分支
git checkout -b <新建分支名称>
需要注意,在切换分支前,要用 git status
命令对当前分支进行检查,以确保在当前分支在干净的情况下进行切换。若在当前分支有修改的情况下切换分支,会导致切换失败,并返回如下错误提示:请在切换分支前,提交你的修改或者暂存修改。
为了更加深入的理解分支,这里引入一个 Git 指针的概念。在 Git 中每提交一个版本,就会保存一个对象。调用时,用指针来调用不同的版本。具体来看如下操作:
点击查看
新建了一个仓库,提交了一个版本 1,
master
指针指向版本 1,HEAD
指针指向master
。之后又提交了一个版本 2,那
master
就会指向版本 2,HEAD
继续指向master
。后面又回退到版本 1,那
master
指针就会重新指向版本 1,而HEAD
始终指向master
。如果我们当前正处于版本 2 上,当输入
git branch develop
新建develop
分支时,Git 会在当前提交的对象上创建一个develop
指针,develop
和master
都指向版本 2,HEAD
继续指向master
。当输入
git checkout develop
切换到develop
分支时,HEAD
指针便指向了develop
。如果我们在
develop
分支上进行提交,develop
指针会指向最新的提交,HEAD
指向develop
,而master
依然指向版本 2 。此时,如果再切换回
master
分支,HEAD
指针就会重新指向master
,同时工作目录恢复成master
指向的版本 2 。
合并不同分支
上面提到源分支和新建分支具有相同的内容和历史,因此 master
分支和 new
分支有相同的内容和历史。假如在 new
分支上做出修改并提交,那么 new
分支就会比 master
分支多出一个历史版本。如果希望将 new
分支的新版本合并到 master
分支就可以执行下面命令:
# 将其他分支合并到当前分支
git merge <其他分支名称>
这里我们来了解一下 Git 内部是如何实现分支合并的,具体下面的流程:
点击查看
这里有一个 Git 仓库,假如现在有一个
develop
分支,我们在这个分支上提交了两个版本,现在我们在develop
分支上又新建了一个demo
分支,此时develop
和demo
指针都指向了最新提交的版本。之后我们在两个分支上进行独立的开发,也都有了新的提交,此时如果想要把
demo
新开发的内容合并到develop
分支上,就需要先切换到develop
分支,再执行git merge demo
,合并后develop
分支上会产生一个新的V1.4
提交,这个提交就是整合了两个分支最新代码的提交。此时,develop
指针会指向最新提交,而demo
指针则保持在合并前的提交上。也就是说,谁请求的合并操作,就在谁那里合并,其他分支不会发生变化。如果是在
demo
分支上执行的合并操作,结果则会相反,在执行完git merge develop
命令之后,新提交会产生在demo
分支上,demo
指针指向合并的提交,develop
指针则保持不变。虽然合并后的代码和上面一样,但是你是在demo
分支上执行的合并,那develop
分支上就没有这个提交记录,很可能会影响后续开发。因此我们在合并时,一定要确定好所在的分支以及合并的方向。还有一种情况,只有
demo
分支有新修改,而develop
分支一直未改动,当我们想把demo
合并到develop
分支上时,需要在develop
分支上执行git merge demo
,此时 Git 内部会将develop
指针直接指向demo
的最新提交,让develop
分支包含demo
中的所有变化,更加快速的完成合并,这种合并方式叫做fast-forward
。
提醒
当我们在执行 git merge
命令时,Git 首先会尝试使用 fast-forward
模式进行合并,如果合并失败,就比如两个分支都有新的修改,那就只能采取创建新提交的方法实现合并。不过这些方法的选择和执行的过程,Git 内部都帮我们实现了,我们只需要输入 git merge
分支名称就够了。
解决分支冲突
在实际开发中,并不是每次都能顺利完成分支合并,因为有时候多个分支可能对同一个文件进行修改,这时合并分支就容易出现冲突。以下就是冲突的产生和解决:
点击查看
- 为了制造冲突产生,我们在
new
分支上修改test.py
文件并提交,然后切换到master
分支上也修改test.py
文件并提交,现在两个分支的test.py
文件有了不同的修改,此时执行git merge new
命令,将new
分支合并到master
分支,就会出现如下存在冲突的提示:
Merge conflict in test.py
在test.py
执行自动合并时发生了冲突。Automatic merge failed
自动合并失败。
- 打开出现冲突的
test.py
文件,出现冲突的内容变成了如下形式:
<<<<<<< HEAD
master分支的修改
=======
new分支的修改
>>>>>>> new
- 这时就需要根据代码实现的功能和代码上下文的逻辑,对代码进行取舍。比如想要同时保留两个分支的修改,就删除冲突标记
<<<<<<<
、=======
和>>>>>>>
,得到如下内容:
master分支的修改
new分支的修改
- 现在解决完冲突后的修改还在工作区,所以要再执行下面命令,将修改提交到版本库才算一次完整的解决冲突流程:
# 提交到暂存区
git add test.py
# 提交到版本库
git commit -m '解决冲突'
删除其他分支
最后,我们来学习一下如何删除分支。删除分支有一个前提条件,就是不能是当前所在分支,即不能在 new
分支中删除 new
分支,只能切换到其他分支中再删除 new
分支。
# 删除分支(如果包含了还未合并的工作或没有对应的远程分支会失败)
git branch -d <分支名称>
# 强行删除分支(删除分支并丢掉未提交的内容)
git branch -D <分支名称>
远程管理
无论是单人独立开发,还是多人协作开发,最终都需要将代码推送到代码托管平台的远程仓库。因此,必须要学习和掌握远程仓库的管理方法。
地址绑定仓库
仓库地址说明
为了方便起见,这里使用 GitHub 平台来创建远程仓库,不会或忘记如何创建远程仓库的请回看上一章节《文档03-GitHub托管平台》中“仓库增删改查”的部分,并创建好远程仓库。仓库创建好以后,在仓库页面会有一个 Clone or download 克隆或下载的按钮,具体说明如下:
点击查看
点击 Clone or download 克隆或下载的按钮,在弹出框中默认会显示以 HTTPS 为协议的仓库地址。
点击上图中弹出框右上角的 Use SSH 按钮后,仓库地址就会切换为 SSH 格式。如果仍需要使用 HTTPS,可以点击弹出框右上角的 Use HTTPS 按钮切换为 HTTPS 格式的仓库地址。
假如使用 HTTPS 格式的地址绑定远程仓库,每次将本地仓库更新推送到远程仓库时,都需要输入 GitHub 用户名和密码进行身份验证,过程就比较繁琐。
假如使用 SSH 格式的地址绑定远程仓库,就需要先配置免密访问,后续每次将本地仓库更新推送到远程仓库时,不用再进行身份验证,非常的方便。
总的来说,无论是 HTTPS 格式的仓库地址,还是 SSH 格式的仓库地址,其作用都是绑定远程仓库,只是在使用上有所区别,具体如下:
特性 SSH HTTPS 地址格式 [email protected]:username/repository.git
https://github.com/username/repository.git
认证方式 密钥对(公钥和私钥) 用户名和密码(或令牌) 易用性 配置稍复杂,后续操作便捷 无需配置,每次需认证 安全性 安全性高,适合长期协作 安全性高,适合偶尔使用 适用场景 长期协作、频繁操作 快速访问、网络受限环境
配置免密访问
考虑到后期会频繁推送更新,所以这里用 SSH 格式的地址来绑定远程仓库会让后期操作更加方便。上面提到,使用 SSH 需要先配置免密访问,配置流程如下:
点击查看
执行下面的命令,一路回车,生成密钥对。
bashssh-keygen -t rsa -b 2048 -C "邮箱"
-t rsa
使用 RSA 算法生成密钥对。-b 2048
密钥强度为 2048,也使用更高强度的 4096。-C "邮箱"
指定邮箱标识,最好和上面用户身份配置的邮箱保持一致。
进入到图中文件生成位置,里面的两个文件就是秘钥对,其中
id_rsa
是私钥(不能泄露出去),id_rsa.pub
是公钥(可以放心地告诉任何人)。使用文本编辑器(例如记事本、Notepad)打开
id_rsa.pub
公钥文件,可以看到里面的内容以ssh-rsa
为开始,以邮箱为结束。打开并登录 GitHub 网站,点击
Settings
选项中的SSH and GPG keys
选项,点击New SSH key
按钮。在 Title 框中写入公钥的备注标题,然后将
id_rsa.pub
公钥文件中的全部内容复制粘贴到 Key 框中,点击Add SSH key
按钮确定添加公钥。完成上述操作后,出现下面的提示就说明 GitHub 账户中成功添加了公钥。
执行下面命令测试 SSH 身份验证,这时服务器会用公钥和本地的私钥进行对接,对接成功后就会出现
Hi username! You've successfully authenticated, but GitHub does not provide shell access.
提示,这就说明成功通过 SSH 身份验证,今后在 GitHub 上进行推送和拉取操作时无需再输入用户名和密码(免密访问)。bash# 测试SSH身份验证 ssh -T [email protected]
提醒
需要用不同的电脑推送更新到远程仓库,只需把每台电脑所生成的公钥添加到托管平台的账户下即可,这样每台电脑就都可以向仓库推送或拉取更新了。
绑定指定仓库
前面新建的远程仓库是一个空仓库,而本地仓库是有内容的,如果希望将本地仓库中的内容推送到远程仓库,需要先通过远程仓库地址将本地仓库与远程仓库绑定,否则 Git 无法确定要推送到哪个远程仓库。具体的绑定流程如下:
点击查看
执行
git remote -v
命令查看本地仓库绑定的远程仓库信息(若无输出,就表明没有绑定远程仓库)。bash# 查看本地仓库绑定的远程仓库的别名 git remote # 查看本地仓库绑定的远程仓库的别名和仓库地址 git remote -v
执行
git remote add
命令将本地仓库与远程仓库进行绑定。bash# 绑定远程仓库 git remote add <远程仓库别名> <远程仓库SSH格式地址>
<远程仓库别名>
远程仓库的别名,默认都用origin
(这里请不要标新立异的用其他别名)。<远程仓库SSH格式地址>
远程仓库的SSH 格式地址。
再次执行
git remote -v
命令就会看到两个origin
,一个是fetch
下载用的,另一个push
上传用的,这就说明本地仓库已经绑定远程仓库。如果想修改远程仓库的别名或移除绑定的远程仓库可以使用下面命令。
bash# 修改远程仓库别名 git remote rename <远程仓库旧别名> <远程仓库新别名> # 移除绑定的远程仓库 git remote remove <远程仓库别名>
提醒
一个本地仓库可以同时绑定多个远程仓库,而且可以是不同平台的远程仓库。
推送远程仓库
三种不同分支
当本地仓库与远程仓库绑定以后,便能将本地仓库的内容推送至远程仓库了。在推送的过程中离不开上面讲到的“分支”,而且会用到三种不同类型的分支,分别是本地分支、上游分支、远程分支,其含义和作用如下:
分支类型 | 所在位置 | 含义 | 作用 |
---|---|---|---|
本地分支 | 本地仓库 | 本地仓库中的分支 | 作为开发人员的工作分支,用于修改代码、开发新功能和修复错误,不影响其他分支。 |
上游分支 | 远程仓库 | 本地分支在远程仓库的关联分支 | 建立本地分支和远程分支的关联,方便进行推送和拉取操作,保证代码更新的同步性和准确性。 |
远程分支 | 远程仓库 | 远程代码托管平台上的分支 | 用于存储团队成员的开发成果,促进团队协作,反映项目的不同开发阶段状态。 |
设置上游分支
理解了三种不同类型分支的作用后,下面就要为本地分支设置上游分支:
点击查看
上面提到本地仓库中,所有修改是放在当前所在的本地分支上的,具体在哪个本地分支回看上面 查看活动分支 的部分。
由于
master
本地分支之前从未向远程仓库推送过内容,也就不存在上游分支,因此执行git push
命令将master
本地分支上内容推送至远程仓库时会返回current branch and set the remote as upstream
提示,意为当前的master
本地分支没有设置上游分支。在没有上游分支的情况下,Git 无法确定推送远程仓库中的哪个远程分支。这时就需要执行上图中提示的
git push --set-upstream
命令为master
本地分支设置上游分支(--set-upstream
的简写是-u
)。设置完成后,在推送时会提示是否接受公钥,输入yes
确认后,Git 会将本地分支与远程分支关联起来,并将。如果远程仓库中没有git push
命令中指定的远程分支,那么 Git 会自动在远程仓库中新建指定的远程分支,再将其与命令中的本地分支进行关联。bash# 将本地分支推送到远程仓库的同名分支,并设置上游分支 git push -u <远程仓库别名> <分支名称> # 将本地分支推送到远程仓库的指定分支,并设置上游分支 git push -u <远程仓库别名> <本地分支名称>:<远程分支名称>
当本地分支与远程分支关联后,下一次执行
git push
命令时就无需指定远程仓库或分支名称,Git 会默认推送到设置的上游分支中。上图中提示
master -> master
说明已经将本地分支master
的内容更新到了远程分支master
。现在打开远程仓库就可以看到远程分支master
上已经增加了本地修改的内容,同时还显示了提交的commit
信息以及提交时间。此后其他人再从远程仓库拉取远程分支master
上的代码时,里面就会包含你推送的内容。
同步远程状态
由于上面远程仓库是新建的空仓库,使用 git push
命令将本地分支的修改推送到远程分支时不会发生冲突。但如果远程分支已有内容,直接推送本地分支的修改可能会引发冲突,因为远程分支可能包含本地分支未同步的更新。因此,在推送修改之前,需要先同步远程分支,确保获取最新内容。如果存在冲突,可以在本地解决后再推送到远程分支。具体操作如下:
点击查看
为了造成远程分支版本超前于本地分支,我们通过代码托管平台在远程分支上进行改动,提交修改后,远程分支出现了新的以
fc2eefe
开头的版本号。执行下面的命令查看本地分支与远程分支的同步状态,提示
master tracked
表示本地分支master
和远程分支origin/master
已经建立了追踪关系,提示local out of date
说明本地分支落后于远程分支。bash# 查看本地分支与远程分支的同步状态 git remote show <远程仓库别名>
执行下面的命令,将远程分支的更新同步到本地分支上。
bash# 更新本地远程分支以及本地分支 git pull <远程仓库别名> <可选:远程分支名>:<可选:本地分支名>
用 git pull
拉取更新后,会看到远程的 master
已经更新到了本地的 origin/master
上,这和我们上面输入 git fetch
得到的日志信息一样,同时还有合并操作的具体信息,这和我们上面输入 git merge
得到的日志信息一样:
执行 git remote show origin
查看同步状态,提示 up to date
说明本地分支和远程分支已经同步:
这里我们可以理解为执行了 git fetch
和 git merge
两步操作
然后我们在本地再次输入 git remote show origin
命令查看更新状态,master tracked
表示本地 master
和远程 master
已经建立了连接**,说明我们需要拉取远程更新到本地了**:
在学习同步远程仓库前,我们还要了解一下不同的分支:
远程分支(Remote Branches):存储在远程仓库(代码托管平台)上的分支。你可以直接在远程仓库上创建一个新的分支,它就是一个远程分支。
本地远程分支(Local Remote Branches):本地仓库对远程仓库某个分支的镜像。假如远程仓库有多个分支,当你从远程仓库克隆一个项目或拉取远程仓库的更改时,这些远程分支会被同步(没有就创建,有就更新)到你的本地仓库中成为本地远程分支,并默认将远程仓库的远程主机名设置为
origin
,你可以使用下面的命令来查看本地远程分支。
# 查看所有的本地远程分支
git branch -r
# 查看所有的本地分支以及本地远程分支
git branch -a
# 向远程仓库推送代码并进行合并
git push
Fetch 拉取
首先,我们通过 git clone
命令将远程仓库克隆到本地生成一个本地仓库,进入本地仓库后,我们执行 git branch -a
命令,获取本地所有分支信息。可以看到,除了默认的 master
本地分支之外,还增加了 origin/HEAD
本地远程指针和 origin/master
本地远程分支。
执行 git log
命令查看版本库信息,和前面查看版本库信息相比,除了 HEAD
本地指针和 master
本地分支之外,同样新增了 origin/HEAD
本地远程指针和 origin/master
本地远程分支,它们都指向了最新版本,说明当前的 master
本地分支和 origin/master
本地远程分支是同步的:
因为远程仓库是由多人维护,随时都有可能更新版本,因此我们需要将远程更新及时拉取到本地,执行下面的命令,此时 Git 会联网查看本地分支和远程分支是否同步,并输出日志信息:
origin
代表了两个 URL 信息,一个是Fetch
拉取代码时的 URL,另一个是Push
推送代码时的 URL。master tracked
表示本地master
和远程master
已经建立了连接。up to date
表示当前本地分支和远程分支是同步状态。
我们执行下面的命令,将远程分支的更新拉取本地远程分支上:
# 更新本地远程分支
git fetch <远程主机名> <可选:远程分支名>
将远程更新拉取到本地后,执行 git log
命令查看版本库信息,可以看到最新的版本中,只有 HEAD
本地指针指向 master
本地分支,之前的 origin/HEAD
本地远程指针和 origin/master
本地远程分支都不见了:
造成上面的原因是因为 git fetch
只更新了本地远程分支,没有更新本地分支,这就导致本地远程分支的版本号超前于本地分支,因此无法显示。但我们希望是将远程更新拉取到本地分支上,那么就要在 master
本地分支上执行一下 git merge origin/master
将本地远程分支合并到本地分支上,这样 master
本地分支上就包含远程的最新状态:
Pull 拉取
警告
执行 git fetch
或 git pull
命令时,Git 会自动更新本地远程分支,以反映远程仓库中对应分支的最新状态。虽然 git pull
比 git fetch
要方便不少,不过 git pull
也存在弊端,容易将本地的修改覆盖掉,因此在拉取远程更新时,我们一定要视情况,谨慎选择你的命令。
克隆远程仓库
克隆远程仓库就是将远程仓库克隆到本地形成一个本地仓库
Clone 绑定:将远程仓库克隆到本地生成一个本地仓库,在这个克隆的过程中远程仓库与本地仓库就已经绑定上了,后续我们就直接在本地仓库中进行开发。具体步骤如下:
- 首先,我们在 GitHub 找到该远程仓库,点击
Clone or download
按钮,在弹出的界面中 HTTPS 或 SSH 协议下的 URL 就是远程仓库的地址,至于它们之间的区别后面再讲,这里我们复制 HTTPS 协议下的 URL 地址:
- 执行下面的命令,将远程仓库克隆至本地生成一个本地仓库:
# 克隆远程仓库
git clone <远程仓库URL地址>
# 克隆远程仓库的指定分支
git clone -b 远程分支名称 <远程仓库URL地址>
- 本地仓库生成后,可以看到目录里多了一个目录,里面的文件内容和远程仓库中的一模一样,并且这个仓库现在已经和远程仓库进行了绑定:
将远程仓库的 SSH 协议的 URL 链接添加到绑定列表中:
更新内容我们就通过 SSH 协议的 URL 来更新了,这样就不用输入密码了:
git clone
仅会克隆远程仓库的默认分支及其完整历史,并设置好该分支的上游分支,其他远程分支仅作为远程引用存储在本地,可根据需要通过特定命令将其转换为可操作的本地分支。
用户生成 SSH 密钥后,将公钥上传到服务器,客户端通过私钥完成认证。认证后无需输入密码,适合频繁操作。
# 查看当前仓库中的所有本地分支和远程分支(包括上游分支)
git branch -a
当然也可以自行在远程仓库中创建好分支,再使用 git push -u
命令将本地分支与远程分进行关联。
合并远程分支
在代码托管平台 GitHub 上也可以进行分支合并,如果说我们在本地新建一个 new
分支,在提交以后,可以看到线上已经多出一个 new
分支:
点击 Pull requests
选项卡,点击 Compare & Pull requests
按钮:
进入页面后,查看是否可以合并,填写合并的请求描述,点击 Create pull request
按钮:
默认进入到讨论板块页面,这里可以进行讨论交流:
提交板块页面可以看到提交的信息:
检查板块可以检查提交的代码:
修改板块可以对照修改的内容:
确认无误后,回到讨论板块,点击 Merge pull request
按钮:
点击 Confirm merge
按钮确认合并:
确认合并后,点击 Delete branch
按钮就可以安全的删除 new
分支了:
回到仓库页会提示合并分支的请求已完成:
再查看分支也只有 master
分支了,说明 new
分支已经成功与 master
分支进行了合并:
提醒
上游分支(upstream branch)是本地分支与远程分支之间的关联,Git 会根据上游分支推送或拉取内容。使用 git branch -vv
可以查看分支与远程分支的关联情况。
警告
如果希望强制推送本地分支的更改到远程仓库,而且需要在你确定覆盖远程仓库上的更改时,可以执行 git push -f <远程主机名> <远程分支名称>
命令,但要小心使用,因为它可能会丢失其他人已经推送到远程仓库的更改。
警告
如果想删除上游分支,可以执行 git branch -r -d 远程仓库别名/上游分支
命令。如果想删除远程分支以及对应的上游分支,可以执行 git push <远程仓库别名> --delete <远程分支>
命令,但在删除远程分支之前需要确定该远程分支不再被需要。
错误排查
密钥类型
有的时候我们使用 git clone [email protected]
链接拉取代码时,可能会报错显示 Git error: no matching host key type found. Their offer: ssh-rsa
提示找不到匹配的主机密钥类型。这里可以通过添加一个 config
文件来解决,内容如下:
Host *
KexAlgorithms +diffie-hellman-group1-sha1
HostkeyAlgorithms +ssh-dss,ssh-rsa
PubkeyAcceptedKeyTypes +ssh-dss,ssh-rsa
密钥变化
有时候我们使用 git clone [email protected]
链接拉取代码时,可能会报错显示下图错误,提示你连接到的远程主机的 SSH 密钥发生了变化,这可能是由于你之前连接过的远程主机的 SSH 密钥发生了变化,或者可能存在中间人攻击的风险。
这里我们先找到与公钥文件同一路径下的 known_hosts
文件:
使用文本编辑器打开 known_hosts
文件,在文件中找到包含远程主机地址的那一行,删除后保存文件并退出。因为我这里连接的是 GitHub 的主机,因此需要找到 github.com
那一行并进行删除,然后保存文件并退出:
现在我们使用 git clone [email protected]
链接拉取代码,会提示你是否愿意将新的主机密钥添加到 known_hosts
文件中,输入 yes
确认,就可以成功拉取代码了。
有些时候,我们在克隆仓库文件或者推送修改时,会提示如下错误:OpenSSL SSL_read: Connection was reset, errno 10054
。这因为 Git 在进行网络通信的时候,会有一个 SSL 证书验证的过程,如果验证失败就会报该错误,我们可以通过如下设置,来关掉 SSL 证书验证:git config --global http.sslVerify "false"
。
这个说法基本正确,但需要注意以下几点:
1. 错误原因
错误 OpenSSL SSL_read: Connection was reset, errno 10054
通常发生在 Git 通过 HTTPS 与远程仓库通信时,可能是以下原因导致的:
- SSL 证书验证失败,例如证书过期或不被信任。
- 网络连接中断或不稳定。
- 防火墙或代理设置阻止了 SSL 连接。
2. 解决方法
通过命令 git config --global http.sslVerify "false"
可以关闭 SSL 证书验证,从而绕过验证问题。这可能解决错误,但不建议长期使用,因为关闭 SSL 证书验证会降低通信的安全性,使你的数据更容易受到中间人攻击(MITM)。
3. 推荐的解决方式
- 首先检查网络连接,确保稳定性。
- 如果证书验证失败,尝试更新 Git 版本,或者手动安装并信任所需的证书。
- 如果在公司内网中使用代理,确保代理配置正确(可以通过
git config
配置代理)。
示例设置关闭验证:
git config --global http.sslVerify "false"
此设置会全局关闭 SSL 验证。为避免影响其他项目,可以改为只针对当前仓库关闭:
git config http.sslVerify "false"
注意事项
关闭 SSL 验证仅适用于调试或临时解决问题,长期使用并不安全。理想情况下,应修复证书或连接问题,而不是绕过验证。
在 Git 的配置选项中有一项 credential.helper=store
信息,用于帮助 Git 存储和管理凭证信息,如用户名和密码。如果凭证信息存储出现问题,这个配置选项可能会导致克隆仓库时出现问题。此时删除该配置选项,即可克隆成功。
这个说法是不完全正确的。以下是详细的分析和说明:
1. 关于 credential.helper=store
的作用
git config credential.helper store
是一个 Git 配置选项,用于将凭据(如用户名和密码)以明文形式存储在本地文件中(通常是 ~/.git-credentials
文件)。启用此选项后,Git 会自动读取凭据,无需每次都手动输入。
2. 凭据存储导致问题的可能性
- 凭据错误:如果
~/.git-credentials
文件中存储了错误的用户名或密码,Git 在尝试克隆或推送时会直接使用这些错误的凭据,导致操作失败。 - 凭据失效:例如,远程仓库的访问凭据(如 Token 或密码)已经失效或被更改,也可能导致克隆失败。
3. 删除该配置选项的影响
- 删除
credential.helper=store
配置后,Git 不再自动读取本地存储的凭据,而是每次操作时手动提示输入用户名和密码。 - 如果克隆成功,说明之前存储的凭据有问题,删除后通过手动输入正确的凭据解决了问题。
4. 推荐解决方法
检查并更新凭据:如果出现问题,可以手动编辑或清空
~/.git-credentials
文件,并确保输入的用户名和密码正确。删除配置选项
:可以删除
credential.helper=store
配置,以避免使用错误的存储凭据:
bashgit config --global --unset credential.helper
使用更安全的方式存储凭据:可以切换到更安全的凭据管理方式,例如
credential.helper=cache
或使用操作系统自带的凭据管理器(如 Windows 的 Credential Manager)。
总结
删除 credential.helper=store
配置选项可能临时解决问题,但并不是最优解。更好的方法是排查并更新存储的凭据信息,确保凭据的正确性。
团队协作开发一定需要一些工具来辅助开发,这里面我们经常说到一个词叫敏捷闭环工具,如果你使用敏捷开发,就应该使用这种敏捷闭环工具来辅助开发。而敏捷闭环工具里面最常提到的一个叫JIRA、一个叫禅道。
禅道是国产的,它有免费版和专业版,专业版是需要支付费用才能使用的,当然功能是非常强大的,不过它的免费版也很强大了,它可以管理敏捷开发的各个环节,从你创建一个产品到添加技术故事到整个开发流程的控制到缺陷管理,包括整个团队的管理,它都能做,功能还是非常强大的。
另外一个商业的软件叫JIRA。建议大家都去了解一下。其中禅道是最容易上手的,首先安装很容易,如果有Linux服务器,它有安装包可以一键安装,装好以后它有一个新手引导教程,教你一步一步如何去使用禅道,而且在禅道官网的上也提供了非常详细的视频,有讲敏捷开发的视频,有讲如何使用禅道的视频。
禅道官网:https://zentao.net
,其中zen就是禅宗的含义
换行风格提示
在执行 git add
命令时可能会出现如下 warning: LF will be replaced by CRLF
关于行末换行符风格的切换提示:
说明该提示出现的原因,还需要回到使用的打字机年代,当时的打字员在输入完一行文字后,进入到一下行的行首,需要执行两个操作:
- 回车(Carriage Return,CR):通过移动打字机的字车(carriage),让打印头回到当前行的开头位置。
- 换行(Line Feed,LF):转动滚筒或使用换行机构,让纸张向上移动一行。
随着科技的发展,计算机逐渐替代了打字机,打字机中的操作也被特定的功能字符所替代,具体如下:
- 回车对应 ASCII 中转义字符
\r
,让光标回到当前行行首的位置。 - 换行对应 ASCII 中转义字符
\n
,让光标在当前位置下移一行。
不仅如此,现代计算机还把“回车+换行”这两个操作合并为了一个操作,使用任何的文本编辑软件实现“回车+换行”,只需按一下 Enter
键即可。但是在不同的操作系统中的“回车+换行”存在着一些差别,说明如下:
在 Windows 系统中,为了兼容旧系统(如 DOS),使用
CRLF
(\r\n
) 表示“回车+换行”。所以在 Windows 系统中使用文本编辑软件(记事本、Notepad 等)按下Enter
键时,会在文件中插入一个CRLF
(回车符 + 换行符,\r\n
)。在 Linux 和 macOS 系统中,由于
CR
(回车)指令很少单独使用,出于简化考虑,直接使用LF
(换行)指令。从技术层面上说,LF
只负责“换行”,但现代的操作系统和文本显示工具在处理LF
时,会将其解释为“回车+换行”的综合动作。所以在 macOS 或 Linux 中使用文本编辑软件(Vim、nano 等)按下Enter
键时,只会在文件中插入一个LF
(换行符,\n
),但在用户体验上,LF
等价于“回车+换行”。
因为不同操作系统中的“回车+换行”实现存在差别,所以在不同操作系统之间传输文本文件,换行符可能会导致显示问题。例如:
- Windows 看到只有
LF
的文件时可能无法正确换行。 - Linux 看到
CRLF
的文件时可能会显示多余的^M
符号。
为了避免上述问题的出现,Git 会检测文件的行末换行符风格与配置是否一致,如果不一致,会自动进行调整并出现如下提示:
- 提示
LF will be replaced by CRLF
:文件的行末符将从LF
转换为CRLF
,这通常发生在 Windows 系统。 - 提示
CRLF will be replaced by LF
:文件的行末符将从CRLF
转换为LF
,这通常发生在非 Windows 系统。
综上所述,如果你在安装 Git 时选择了自动检测和修改换行符,Git 就会在与配置不一致的情况下,自动调整行末换行符风格并进行提示,无需特别处理。