河南网站建设哪家公司好,网站建设的实验报告,网上营销培训课程,php网站开发经理招聘一、什么是 Git
1. 何为版本控制
版本控制是一种记录文件变化的系统#xff0c;可以跟踪文件的修改历史#xff0c;并允许用户在不同版本之间进行比较、恢复或合并。它主要用于软件开发过程中管理代码的变更#xff0c;但也可以应用于任何需要跟踪文件变更的场景。
版本控…一、什么是 Git
1. 何为版本控制
版本控制是一种记录文件变化的系统可以跟踪文件的修改历史并允许用户在不同版本之间进行比较、恢复或合并。它主要用于软件开发过程中管理代码的变更但也可以应用于任何需要跟踪文件变更的场景。
版本控制系统VCS可以帮助团队协作开发并提供以下功能
历史记录管理版本控制系统会记录每个文件的修改历史包括修改内容、时间和作者等信息。这使得用户可以查看文件的演变历程了解每次修改的目的和影响。并行开发支持多个开发者可以同时修改同一个项目的不同部分版本控制系统能够合并这些修改并解决可能出现的冲突。备份和恢复通过版本控制系统可以轻松地恢复到之前的任意版本即使文件丢失或损坏也能够通过版本控制系统进行恢复。分支管理版本控制系统允许创建分支Branch开发者可以在分支上进行实验性的修改而不会影响到主要的代码库这使得并行开发和功能开发更加灵活。版本标记可以对重要的版本进行标记Tag例如发布版本或里程碑版本方便日后查找和引用。
常见的版本控制系统包括 Git、SubversionSVN、Mercurial 等它们都提供了类似的功能但在实现方式和使用上有所不同。其中Git 是目前最流行的版本控制系统之一被广泛应用于软件开发领域。
2. Git 的诞生 Git 的开发由 Torvalds林纳斯·托瓦兹Linux 之父于 2005 年 4 月启动起因是 2002 年时用于 Linux 内核开发的专有源代码控制管理SCM系统 BitKeeper 撤销了其 Linux 开发的免费许可证。BitKeeper 的版权所有者 Larry McVoy 声称Andrew TridgellLinux 社区主要贡献者Samba之父通过逆向工程破解了 BitKeeper 的协议并创建了SourcePuller。 同一事件还刺激了另一个版本控制系统 Mercurial 的创建。 引用Git - Wikipedia Git 的诞生是由于 Linux 内核社区与 BitKeeper 公司之间发生了一些纠纷导致 Linux 内核社区无法继续免费使用 BitKeeper。
由于这个事件Linus Torvalds 决定开始开发一个新的版本控制系统这个系统要具有以下特点
分布式与传统的集中式版本控制系统不同分布式版本控制系统允许每个开发者在本地完整地拷贝整个代码库这样可以在没有网络连接的情况下继续工作并且更容易支持分支和合并操作。性能由于 Linux 内核的规模巨大所以版本控制系统必须具备高效的性能能够快速处理大量的文件和提交。简单易用Git 设计的目标之一是让用户更容易理解和使用尽管它提供了丰富的功能但命令和概念相对简单学习曲线较为平缓。
基于这些需求Linus Torvalds 开始了 Git 的开发并于 2005 年 4 月 3 日发布了第一个版本。随着时间的推移Git 在开源社区中得到了广泛的认可和应用逐渐成为了最流行和最常用的版本控制系统之一。
3. 集中式 VS 分布式
集中式版本控制系统Centralized Version Control SystemCVCS和分布式版本控制系统Distributed Version Control SystemDVCS是两种不同类型的版本控制系统它们在数据存储、工作流程和协作模式等方面有所不同。
3-1. 集中式版本控制系统CVCS
CVCS 是一种传统的版本控制系统所有的文件和版本历史都存储在中央服务器上。开发者需要通过从中央服务器检出代码checkout来获取项目的副本在本地进行修改后再提交到中央服务器。典型的 CVCS 包括 CVSConcurrent Versions System和 SVNSubversion等。 主要特点包括
协作依赖于中央服务器开发者必须与中央服务器保持连接才能进行代码的获取和提交。危险点如果中央服务器发生故障或者网络不稳定那么开发者将无法工作。分支和合并相对复杂通常需要由中央服务器来执行分支和合并操作。
集中式版本控制系统最大的问题就是必须联网才能工作遇到带宽不好的情况是提交、更新较大文件可能会十分的缓慢。而且集中式版本控制系统容灾性差万一中心服务器硬盘出现的数据丢失那后果是很严重的。
3-2. 分布式版本控制系统
DVCS 是一种新型的版本控制系统每个开发者都拥有完整的代码仓库包括完整的历史记录的副本。开发者可以在本地进行大部分的操作而不需要与中央服务器保持连接。典型的 DVCS 包括 Git、Mercurial 等。 主要特点包括
分布式架构每个开发者都可以在本地进行工作不受中央服务器的限制。离线工作开发者可以在没有网络连接的情况下继续工作而且操作效率更高。分支和合并更灵活由于每个开发者都拥有完整的历史记录因此分支和合并操作更加轻松和快速。
分布式版本系统的优点显而易见首先它可以完全独立的工作不需要服务器的参与其次它具有很高的安全性某一台电脑的数据丢失后可以通过其他电脑进行恢复。
现实情况下分布式版本控制系统也需要一个“中心服务器”但该服务器的作用仅限于为数据的交互提供便利性。
4. Git 的工作流程
Git 的命令大全和作用可以从这个网站查阅workspace :: Git Cheatsheet :: NDP Software 克隆仓库在开始工作之前首先需要将远程仓库克隆到本地机器上。使用git clone命令可以将远程仓库完整地复制到本地。创建分支为了并行开发和隔离不同的功能或修复通常会创建新的分支。使用git branch命令可以创建一个新的分支例如git branch feature-branch。切换分支使用git checkout命令可以切换到创建的分支例如git checkout feature-branch。添加和提交更改在所选分支上进行工作后可以使用git add命令将更改的文件添加到暂存区然后使用git commit命令将暂存区的更改提交到本地仓库推送更改如果要将本地分支的更改推送到远程仓库可以使用git push命令例如git push origin feature-branch。这将把本地分支的更改推送到远程仓库中对应的分支。合并分支当分支的工作完成后可以将其合并到主分支通常是master分支或其他目标分支中。使用git merge命令可以执行分支合并解决冲突如果在合并分支时发生冲突即同一文件的不同部分具有不同的更改需要手动解决冲突。在冲突解决后再次执行git add和git commit命令以完成合并。拉取更新在团队协作中如果其他人推送了更改到远程仓库可以使用git pull命令拉取更新到本地仓库以确保与最新代码保持同步
二、Git 的下载与安装
1. Git 的下载
Git 的下载链接Git - Downloads (git-scm.com) 点击进链接后可以看到有四个版本按操作系统位数可分为 32 位和 64 位按使用版本可以分为 Standalone Installer独立安装程序和 Portable便携式可以简单的理解为前者需要安装后者不需要安装可以直接使用国内对这类不需要安装的软件也称为绿色版。 本文使用的是 64 位的独立安装版本。
2. Git 的安装
以管理员身份运行 Git 安装程序。 直接点Next。 我这里选择默认的安装路径可以根据自己的喜好更改路径然后点击Next。 按默认的勾选安装即可。 直接点Next。 选择文件的编辑器一般都是用 VIM 作为默认的编辑器直接点Next。 决定初始化新项目仓库的主干名字第一种是让 Git 自己选择即默认名字是 master 但是未来也有可能会改为其他名字第二种是用户自行决定默认是 main当然你也可以改为其他的名字。一般默认第一种点击Next。 [!NOTE] 这个安装流程在之前是没有的第二个选项下面有个NEW说很多团队已经重命名他们的默认主干名为 main。起因是 2020 年非裔男子 George Floyd 因白人警察暴力执法惨死而掀起的 Black Lives Matter黑人的命也是命运动很多人认为 master 不尊重黑人呼吁改为 main。 接着是调整 PATH 环境变量默认使用第二个。第三个只适合懂的人折腾。 [!NOTE] 翻译如下 仅从 Git Bash 使用 Git 这是最谨慎的选择因为您的 PATH 根本不会被修改。您将只能使用 Git Bash 中的 Git 命令行工具。 从命令行以及第三方软件进行 Git 推荐此选项仅将一些最小的 Git 包装器添加到PATH中以避免使用可选的 Unix 工具使环境混乱。您将能够使用 Git Bash 中的 Git命令提示符和 Windov PowerShell 以及在 PATH 中寻找 Git 的任何第三方软件。 使用命令提示符中的 Git 和可选的 Unix 工具 Git 和可选的 Unix 工具都将添加到您的 PATH 中。 警告这将覆盖 Windows 工具例如 “find” and “sort”. 仅在了解其含义后使用此选项。 选择 SSH 执行文件按默认的即可。 [!NOTE] 以前的 Git 2.31 都没有这个界面估计是 2.4 开始可以使用外部的 SSH 文件。 选择HTTPS后端传输还是按默认的来点击Next。 [!NOTE] 作为普通用户只是用 Git 来访问 Github、GitLab 等网站选择前者就行了。如果在具有企业管理证书的组织中使用 Git则将需要使用安全通道。如果你仅使用 Git 来访问公共存储库例如 GitHub 或者你的组织不管理自己的证书那么使用 SSL 后端它们只是同一协议的不同实现就可以了。 两个选项的具体区别感兴趣的可以去程序员社区 stack overflow 查看链接git - What’s the difference between OpenSSL and the native windows Secure Channel library - Stack Overflow 配置行尾符号转换 这三种选择分别是 ① 签出 Windows 样式提交 Unix 样式的行结尾② 按原样签出提交Unix样式的行结尾③ 按原样签出按原样提交。 那 Windows 样式和 Unix 样式到底有什么区别呢 GitHub 中公开的代码大部分都是以 Mac 或 Linux 中的 LFLine Feed换行。然而由于 Windows 中是以 CRLFCarriage Return Line Feed换行的所以在非对应的编辑器中将不能正常显示。 Git 可以通过设置自动转换这些换行符。使用 Windows 环境的各位请选择推荐的 “Checkout Windows-stylecommit Unix-style line endings” 选项。换行符在签出时会自动转换为 CRLF在提交时则会自动转换为 LF 。 引用《GitHub 入门与实践》 上面说 Mac 、Linux、Unix 的 Line Feed 翻译过来就是换行符用 “\n” 表示换行符 “\n” 的 ASCII 值为 0x0A。而 Windows 的是 Carriage Return Line Feed回车 换行用 “\r\n” 表示回车符 “\r” 的 ASCII 值为 0x0D。
我们现在的教程就是介绍怎么安装 Windows 版 Git所以肯定选第一项啦。
至于 “回车”carriage return和 “换行”line feed这两个概念的来历和区别 在计算机还没有出现之前有一种叫做电传打字机Teletype Model 33的玩意每秒钟可以打 10 个字符。但是它有一个问题就是打字机打完一行换行的时候要用去 0.2 秒正好可以打两个字符。要是在这 0.2 秒里面又有新的字符传过来那么这个字符将丢失。 于是研制人员想了个办法解决这个问题就是在每行后面加两个表示结束的字符。一个叫做回车告诉打字机把打印头定位在左边界另一个叫做换行告诉打字机把纸向下移一行。 参考程序员社区 stack overflow链接newline - What are carriage return, linefeed, and form feed? - Stack Overflow 接着来到配置终端模拟器与 Git Bash 一起使用建议选择第一个选项因为 MinTTY 3功能比 cmd 多cmd 只不过比 MinTTY 更适合处理 Windows 的一些接口问题这个对 Git 用处不大除此之外 Windows 的默认控制台窗口有很多劣势比如 cmd 具有非常有限的默认历史记录回滚堆栈和糟糕的字体编码等等。
相比之下MinTTY 具有可调整大小的窗口和其他有用的可配置选项可以通过右键单击的工具栏来打开它们 git-bash 。 接着来到选择默认的 “git pull” 行为的界面先解释一下 git pull 是什么意思git pull 就是获取最新的远程仓库分支到本地并与本地分支合并。这里给出三个 git pull 的行为分别是merge、rebase 和直接获取。 一般默认选择第一项git rebase 绝大部分程序员都用不好或者不懂而且风险很大但是很多会用的人也很推崇但是用不好就是灾难。git pull 只是拉取远程分支并与本地分支合并而 git fetch 只是拉取远程分支怎么合并选择 merge 还是 rebase可以再做选择。
更详细的解释我给各位总结了几个比较好的说明以下是链接 git branch - Why does git perform fast-forward merges by default? - Stack Overflow In git how is fetch different than pull and how is merge different than rebase? - Stack Overflow Difference between git pull and git pull --rebase - Stack Overflow 接着是选择一个凭证帮助程序 第一个选项是提供登录凭证帮助的Git 有时需要用户的凭据才能执行操作例如可能需要输入用户名和密码才能通过 HTTP 访问远程存储库GitHubGItLab 等等。第二个则是不使用凭证助手。
建议默认点击Next。 然后是配置额外的选项分别是启用文件系统缓存和启用符号链接。启用文件系统缓存就是将批量读取文件系统数据并将其缓存在内存中以进行某些操作可以显著提升性能。这个选项默认开启。启用符号链接 符号链接是一类特殊的文件 其包含有一条以绝对路径或者相对路径的形式指向其它文件或者目录的引用类似于 Windows 的快捷方式不完全等同类 Unix如 Linux 下的符号链接。因为该功能的支持需要一些条件所以默认不开启。
建议默认点击Next。 最后是配置实验性选项这些还是实验性功能可能会有一些小错误之类的建议不用开启。
直接点Install安装。 安装完成默认勾选的选项都去掉点击Finish完成安装。 3. Git 各个软件的简单介绍
安装好后可以在开始菜单中找到 Git 的文件夹里面包含了 Git Bash、Git CMD、Git FAQs、Git GUI、Git Release Note下面我们就分别介绍一下这几个软件。 3-1. Git Bash
Git Bash 是基于CMD的在CMD的基础上增添一些新的命令与功能平时主要用这个功能很丰富。 3-2. Git CMD
Git CMD 不能说和 cmd 完全一样只能说一模一样功能少得可怜。 3-3. Git FAQs
Git FAQs 就是 Git Frequently Asked Questions常问问题点击图标直接就可以访问地址FAQ · git-for-windows/git Wiki (github.com)
3-4. Git GUI
Git GUI 就是 Git 的图形化界面可以通过它快速创建新仓库项目克隆存在的仓库项目打开存在的仓库仓库。这个我基本没用过建议还是用命令行学习Git。 3-5. Git Release Note
Git Release Note 就是版本说明增加了什么功能修复了什么 bug 之类的。一般使用浏览器打开。 三、Git 本地管理
1. 创建版本库
版本库repository又名仓库本质上就是一个目录文件夹把需要控制管理的代码、文档放入到该目录下。在该目录下的所有文件的操作添加、删除、修改、回退、查询等都可以被管理起来。
为方便演示我新建了一个文件来进行操作而这个新建的文件夹就可以被称为版本库但是此时还不是真正意义上的版本库它跟普通的文件夹没有区别。 [!CAUTION] 为了避免遇到各种莫名其妙的问题请确保目录名包括父目录不包含中文。 进入文件夹右键弹出菜单选择“Open Git Bash here”。 输入git init并回车可初始化版本库执行成功后这个文件夹才是真正 Git 版本库。同时在文件夹中会多出一个隐藏文件夹名为“.git”。 当版本库创建好后我们就可以用它来做项目的管理了。例如在初始化版本库后通常都会添加一个说明文件来描述这个版本库的作用行业默认都是新建一个名为“README”的.txt文件普通文本文件或者.md文件Markdown 文件。
我这里直接就在 Git 的命令框中创建了使用的是touch命令创建再用vim命令进入编辑如果你不会 VIM可以用最简单的右键新建文本文件的方法新建不过我还是建议学习一下 VIM。 在里面我输入了“This is a version library for learning Git.”然后保存退出。 可以使用cat命令查看“README.md”。 根据 Git 的工作流程此时的 README.md 还在工作区可以通过git status命令查看仓库的状态。下图中红色字体的“README.md”就是新增或者被修改的文件目前还在工作区还未提交到暂存区括号里也提示我们可以用git add来添加到暂存区。 使用git add README.md把 README.md 添加到 Git 的暂存区。 再次用git status命令查看仓库的状态就可以看到 README.md 变成了绿色已经保存到暂存区了。 根据 Git 的工作流程此时 README.md 也只是存在暂存区还并没有提交到仓库需要使用git commit命令将 README.md 提交到仓库。
我在使用git commit命令时会加上参数-m用于表示本次提交的说明信息说明信息最好统一格式这样对于后续的 Bug 管理、版本信息追踪都会大有好处。
不过我们第一次使用时会发现无法提交如下图。 这个问题是由于 Git 无法检测到用户身份信息而引起的Git 在每次提交时都需要知道提交者的姓名和电子邮件地址。可以按照命令行中提供的提示来设置用户身份信息。如下
git config --global user.email youexample.com
git config --global user.name Your Name把双引号里面内容替换掉我这里替换成我的 QQ 邮箱和英文名如下图所示 再次提交就可以了。 这时使用git status命令查看仓库的状态已经没有修改或新增的文件在暂存区或者工作区了。 2. 提交修改
提交修改的步骤和前面提到的一样例如我们给现有的 README.md 文件修改一些内容如下图 这时使用git status命令查看仓库的状态 上面的输出表示 README.md 已经修改但是还未添加到暂存区。我们可以通过git diff查看当前的修改内容输出格式为 Linux 内核补丁所用的格式 新增的内容会以绿色字体显示并且在每行开头多一个表示新增内容如果是-则表示删除内容且字体为红色。
现在提交修改的内容使用的命令还是git add和git commit。 可以通过git log查看近期的提交记录提交记录以时间倒叙输出。 如果执行git commit之后发现提交说明写的有问题可以通过git commit --amend命令修改最近一次的提交说明。输入命令后会用 VIM 编辑器打开提交信息还是要会 VIM 不会的先去查一下怎么用。 修改一下提交记录如下图修改的内容没什么特殊含义只是为了示范 保存退出后再次用git log查看提交记录可以看到提交信息已经更新了。 如果要添加代码文件也是类似的操作例如新增一个.c 文件。 随便写一些内容。 只要有新文件加进来都可以用git status命令看到。 顺便编译一下程序多一个可执行文件。 用git status命令查看仓库。 提交修改的内容使用的命令还是git add和git commit但是同时提交两个或两个以上的文件有两种办法。 方法一 git add命令后面把要添加的文件全部写上不同文件用空格隔开。 方法二 如果要添加的文件是git status能看到的全部红色字体的文件直接用git add .在原来的命令上加一个.即可。
然后用git commit提交到仓库即可。 这时用git log查看提交记录就有三段记录了。 3. 版本回退
在实际产品开发过程中我们需要养成一个习惯那就是代码或文档修改到一定程度时要将修改的内容提交到版本库。这样一旦后续代码或文档修改出了问题或者误删了某些文件还可以通过最近的一次 commit 进行恢复这个恢复的过程就是版本回退。
在原来的 README.md 中我重新做了修改如下图 并且做了提交如下图 可以通过git log加上--prettyoneline参数查看简化版的提交记录信息 可以看到有四条提交记录前面黄色的数字是 commit ID是 Git 的提交的版本 ID跟 SVN 递增的 ID 不一样通常是 40 个字符的十六进制字符串这是 SHA-1 哈希算法生成的。这个 ID 通常被称为SHA-1 哈希或SHA-1。SHA-1 是一种加密哈希函数虽然在安全上已经存在一些漏洞但在 Git 中仍然被广泛使用。
回到正题由于某种原因需要将版本库回退到“Added code and executable files.”这次的提交版本可以使用git reset --hard命令来回退。但是有个前提就是要让 Git 知道当前是什么版本而且还要知道回退到哪个版本。
在 Git 中用 HEAD 来表示当前版本HEAD^ 表示上一个HEAD^^ 表示上上一个版本HEAD~100 表示往上 100 个版本。由上图可知当前的 HEAD 就指在最新提交的版本上回退到“Added code and executable files.”这次的提交版本就是当前版本的上一次那么执行下面命令即可
git reset --hard HEAD^执行结果如下图 打开 README.md 查看一下果然回退了。 那么假设过了一段时间需要返回之前的版本也非常简单只要能找到之前版本 commit ID就可以通过git reset命令回退就行。Git 提供了git reflog命令用于记录每次的命令那么就可以通过git reflog找到之前版本的 commit ID。 上图红框标记的就是最后一次的版本提交记录最开始的那串数字就是 commit ID执行下面的命令即可。
git reset --hard 6353f69执行结束用git log可以看到版本已经复原了。 查看一下文件内容也是复原状态。 Git 的版本回退速度之所以非常快速主要是 Git 在内部有个指向当前版本的 HEAD 指针回退版本时Git 仅仅只是把 HEAD 从指向需要回退的版本。 4. 理解工作区和暂存区
在使用 Git 管理版本时经常会听到工作区和暂存区这两个概念理解工作区和暂存区对于理解 Git 的很多操作十分有帮助。从前面提到的 Git 的工作流程可以看出工作区和暂存区都包含在本地仓库里面下面分别介绍一下这两个概念。
4-1. 工作区
当前开发程序所在目录称为工作区比如我前面演示用的文件夹“git_test”就是一个工作区可以在这里新增或删除文件也可以修改文件里面的内容。
这里特别强调一下隐藏文件夹“.git”不属于工作区因为里面的内容不可以被用户修改用户也不能在这个文件夹里新增或删除。它跟接下来要讲的暂存区有很大的关系。 4-2. 暂存区
先着重讲一下隐藏文件夹“.git”它是 Git 版本控制系统的核心所在它包含了项目的版本控制信息包括提交历史、分支、标签等。“.git”文件夹的作用如下
版本控制信息存储“.git”文件夹保存了项目的整个版本控制历史包括每个提交的内容、作者、时间等信息。分支和标签管理Git 使用“.git”文件夹来存储和管理分支和标签信息以便于在不同的分支之间切换或者查看特定标签的快照。配置信息Git 的配置信息也存储在“.git”文件夹中包括用户信息、远程仓库地址等。暂存区“.git”文件夹包含一个暂存区stage也叫 index用于暂存待提交的修改以便在提交时将其纳入版本控制。工作树状态追踪Git 通过“.git”文件夹来追踪工作树working tree中文件的状态以便确定哪些文件已经修改、添加或删除。
综上所述我们可以知道暂存区就在“.git”里面还有 Git 为我们自动创建的第一个分支 master以及指向 master 的一个指针 HEAD。 分支和 HEAD 的概念我们以后再讲前面讲了我们把文件往 Git 版本库里添加的时候分别用git add和git commit两步执行。其实本质上就是用git add把文件修改添加到暂存区然后用git commit提交更改把暂存区的所有内容提交到当前分支。因为我们创建 Git 版本库时Git 自动为我们创建了唯一的 master 分支所以git commit就是往 master 分支上提交更改。
可以简单理解为需要提交的文件修改通通放到暂存区然后一次性提交暂存区的所有修改。
4-3. git diff 比较域
git diff命令可以对比两个版本的差异一般会结合一些常用的参数运行如下图所示 由上图可知git diff是只比较比较工作区和暂存区最后一次 add的区别git diff --cached是只比较暂存区和版本库的区别git diff HEAD 是只比较工作区和版本库最后一次 commit的区别。
5. 管理修改
Git 之所以比其他版本控制系统性能优秀主要就是它跟踪并管理的是修改即每次版本之间的差异而不是简单的文件。
例如我现在重新修改一下 README.md 的内容新增内容如下图 通过git status命令可以查看被修改的文件再通过git diff可以对比工作区和暂存区的不同。 然后git add之后再次查看仓库状态。 先暂时不提交到 master 分支再次修改 README.md修改如下图 然后不进行git add直接用git commit提交此时可以发现提交是成功的但是用git status查看还是存在工作区有被修改的文件。显然第二次的修改并没有被提交。 那就证明了 Git 管理的是修改当用git add命令后在工作区的第一次修改被放入暂存区并做好了被提交的准备但是在工作区的第二次修改并没有放入暂存区所以git commit只负责把暂存区的修改提交了也就是第一次的修改被提交了第二次的修改不会被提交。
可以通过git diff HEAD和git diff --cached分别查看仓库的情况可以看出工作区的文件和仓库的文件是有差别的而暂存区和仓库的并没有区别。 6. 撤销修改
实际的项目开发过程中难免会犯错比如 Bug 改错了位置、代码注释写错了等等。出现了错误的修改之后就要及时的撤销修改下面分别介绍不同场景下的撤销操作。
6-1. 撤销工作区的修改
假如因为某些原因我们要放弃这一次工作区的修改可以用git checkout命令撤销修改。先使用git status命令查看一下当前版本库的状态当前有一个工作区的修改。 以前版本的 Git第二行的括号里提示的是git checkout现在提示的是git restore。这两个都是 Git 中用于撤销更改或者切换版本的命令但是它们的使用方式和作用略有不同。
git checkout 用法git checkout branch 切换分支git checkout commit file 恢复单个文件到指定提交版本。主要功能 切换分支将工作区的内容切换到指定分支的最新状态包括所有文件和文件夹。恢复文件将单个文件的状态恢复到指定提交版本的状态。 git restore 用法git restore file 恢复工作区中的文件到暂存区的状态git restore --sourcecommit file 恢复单个文件到指定提交版本git restore --staged file 将暂存区的文件恢复到工作区。主要功能 恢复工作区将工作区中的文件恢复到暂存区或指定提交版本的状态。将暂存区的文件恢复到工作区。
总的来说git checkout 主要用于切换分支和恢复单个文件而 git restore 则用于恢复工作区中的文件到指定状态同时也可以用来操作暂存区的文件。两者的功能有交叉但是在实际使用时需要根据具体的情况选择合适的命令。
这里我用git checkout -- README.md来撤销修改再次用git status查看工作区已经clean了并且 README.md 已经恢复到了版本库的版本。 6-2. 撤销暂存区的提交
对 README.md 再修改一些内容并用git add提交到暂存区。 使用git status查看当前版本库的状态可以看到修改已经提交到了暂存区。 如果我们想撤销这次的提交可以看到在上图中有个 git 命令已经给出了提示可以用git restore来撤销之前的 Git 的版本是提示使用git reset来撤销我们还是用更广为人知的git reset来撤销。
输入git reset HEAD README.md表示我们要从 HEAD 只向的分支恢复 README.md 文件。 6-3. 撤销本地版本库的提交
如果已经把修改后的文件提交到 master 分支上了想要撤销是撤销不了的只能通过回退版本第 3 小节的内容的方式来进行。现在还只是本地版本库如果后续学到远程版本库把修改提交到远程版本库那可能就无力回天了因为远程版本库不止一个人在用可能会影响到其他人的工作。
因此很多企业在合入远程仓库之前都会有一个环节叫代码审查review所谓的 review 就是指开发团队中的成员对其他成员编写的代码进行检查和评审的过程。代码审查是一种质量保证机制通过让其他团队成员检查代码可以发现潜在的问题、提供反馈和建议以确保代码质量和项目整体的稳定性。
7. 删除文件
在实际工作中删除文件也是常见的操作在 Windows 系统常常使用手动删除习惯使用命令行的同学会使用rm。例如我们现在就把其中一个文件删除并查看当前版本库的状态。 Git 已经感知到了工作区的 Hello.exe 被删除但版本库中的文件还未删除这时我们有两种选择 使用git add/rm file命令更新修改到暂存区以备 commit。 这个操作目的是为了删除文件做准备如果在执行git rm之前突然不想删除了那就是第二种选择了。 使用git checkout -- file丢弃工作区的修改即还原被删除的文件。
8. 查看提交历史
在提交了若干更新又或是克隆了某个项目之后通常都会回顾下提交历史看看项目都做了哪些修改。而查看历史提交记录最简单而又有效的工具是git log命令了。
我们去嵌入式大神稚晖君的 GitHub 上找一个项目并克隆下来这是他的 Github 链接peng-zhihui (稚晖) (github.com)。 我这里克隆了他的 HelloWord-Keyboard 项目使用了git clone命令。 我这里换了一个文件夹并且在这个文件夹下重新打开了 Git 终端克隆项目完成后运行 Git 的目录下会多出一个项目为名的文件夹。 用cd命令进入文件夹。 在此项目中运行git log命令时可以看到很多历史记录。 历史记录甚至多到滚动不完可以按q键退出浏览历史记录。
不传入任何参数的默认情况下git log会按时间先后顺序列出所有的提交最近的更新排在最上面。会包含每个提交的 commit ID、作者的名字、电子邮箱、提交时间和提交说明。 git log有许多选项或者叫参数可以有效搜索信息例如需要显示每次提交所引入的差异就可以加入参数-p或--patchpatch 就是我们常说的补丁。 如果要限制显示的日志条目数量可以输入短杆后加上数量例如git log -3就会显示最近三次的提交信息。 如果想看到每次提交的简略统计信息可以使用–stat选项。 –stat选项在每次提交的下面列出所有被修改过的文件、有多少文件被修改了以及被修改过的文件的哪些行被移除或是添加了。 在每次提交的最后还有一个总结。
关于git log还有很多选项这里就不一一赘述了感兴趣的直接在这个链接学习Git - git-log Documentation (git-scm.com)。
四、代码托管平台
1. 常见的代码托管平台 GitHub: GitHub 是最大的开源代码托管平台之一提供了强大的版本控制功能和协作工具支持 Git。 GitLab: GitLab 是一个开源的代码托管平台提供了类似于 GitHub 的功能但还包含了一些额外的功能例如持续集成、CI/CD 等。 Gitee: Gitee码云是开源中国OSCHINA的一个类似于 GitHub 的代码托管平台支持 Git 和 SVN提供 Git 仓库托管、团队协作、代码审查、问题跟踪、持续集成等功能。它是由中国的一家公司开发和运营旨在为国内开发者提供一个方便、高效的代码托管平台。 Bitbucket: Bitbucket 是由 Atlassian 公司提供的代码托管平台支持 Git 和 Mercurial提供了私有仓库、团队协作、持续集成等功能。 SourceForge: SourceForge 是一个老牌的开源软件托管平台提供了代码托管、下载、论坛等功能支持 Git、SVN 和 Mercurial。 Microsoft Azure DevOps: Azure DevOps之前称为 Visual Studio Team Services 或 VSTS是微软提供的一套全面的软件开发工具包括代码托管、CI/CD、项目管理等功能。 AWS CodeCommit: AWS CodeCommit 是亚马逊提供的托管 Git 存储库的服务可以与其他 AWS 服务集成例如 AWS CodePipeline 和 AWS CodeBuild。 Google Cloud Source Repositories: Google Cloud Source Repositories 是 Google Cloud Platform 提供的托管 Git 存储库的服务可以与其他 Google Cloud 平台的服务集成。 2. 注册代码托管平台账号
代码托管平台较多这里只介绍其中两个平台的账号申请而且都是以官方的文档为主要参考教程分别是 GitHub 和 Gitee。推荐 GitHub 的原因是大部分开源项目都在 GitHub 上包括 Linux kernel。但毕竟 GitHub 的服务器在国外国内访问很不方便无法访问和超慢的响应时间是家常便饭要正常访问还需要翻墙这事违法哈。所以这里补充一个类型 GitHub 的国内平台就是开源中国的 Gitee同样可以有很多开源项目也可以在 Git 上配置账号与 GitHub 的体验差别不大主要对英语不好同学很友好基本都是中文的。
2-1. GitHub
注册 GitHub 账号建议先翻墙然后看官方文档。
官方注册文档链接《在 GitHub 上创建帐户》
2-2. Gitee
官方注册文档链接《注册 Gitee 账号》
3. 为 GitHub / Gitee 配置公钥
3-1. GitHub 配置 SSH Key
在开始菜单中打开 Git Bash。 设置用户信息 git config --global user.name your name
git config --global user.email your e-mail把双引号里面的内容替换成自己的如下图 创建SSH密钥 ssh-keygen -t rsa -b 4096 -C your e-mail同样把双引号的内容替换成自己的邮箱不过创建密钥时会遇到一个界面静止不动如下图 这是要我们设置一下密码这个 Key 我们只是个人使用只要不设计军事项目不需要密码直接按三次回车跳过。 看到这个画面就是 Key 创建成功了。 复制公钥在当前窗口输入命令cd .ssh回车再输入ls可以看到“.ssh”文件夹下生成了 Key 的密钥对。 其中id_rsa 是私钥不能泄露出去 是公钥可以放心地告诉任何人。输入cat id_rsa.pub查看公钥并复制。 登录 GitHub 网站点击网页右上角的头像在弹出的菜单中选择Setting。 在左边的选项卡中选中SSH and GPG keys然后点击右边绿色按钮的New SSH key。 然后输入 Title再粘贴密钥最后点击下面的按钮Add SSH key。 有时候可能要验证一下是不是本人操作输入 GitHub 的密码验证就好了。 成功添加 Key 如下图 测试连接输入下面的命令行 ssh -T gitgithub.com如果看到类似于 “Hi username! You’ve successfully authenticated, but GitHub does not provide shell access.” 的消息则说明 SSH 连接已成功设置。如下图 [!NOTE] 为什么 GitHub 需要 SSH Key 呢 因为 GitHub 需要识别出你推送的提交确实是你推送的而不是别人冒充的而 Git 支持 SSH 协议所以 GitHub 只要知道了你的公钥就可以确认只有你自己才能推送。 当然GitHub 允许你添加多个 Key。假定你有若干电脑你一会儿在公司提交一会儿在家里提交只要把每台电脑的 Key 都添加到 GitHub就可以在每台电脑上往 GitHub 推送了。 3-2. Gitee 配置 SSH Key
Git 配置 Gitee 和配置 GitHub 过程一样但是不能使用同一个公钥所以这里需要生成新的 Key 的密钥对。
为了区分之前的 Key我们用基于ed25519算法的 SSH 密钥。
ssh-keygen -t ed25519 -C your e-mail生成的密钥对如下 用同样的方法复制密钥然后登录 Gitee 网站和账号点击右上角头像弹出菜单选择“设置”。 在左边的选项卡中选择 SSH 公钥的选项卡输入标题和公钥点击确定。 然后输入密码验证一下。 公钥成功添加。 测试方法和 GitHub 差不多后面部分改成 gitee.com 就行。中间停顿的时候输入yes回车即可看到类似于 “Hi username! You’ve successfully authenticated, but GitHub does not provide shell access.” 的消息则说明 SSH 连接已成功设置。 4. 添加远程仓库
到目前为止我们已经学会了很多的关于 Git 的基本操作。但是这些功能与之前的 SVN 之类的集中式版本控制系统相比也没有太大的差别因为这些仅仅只是基本操作而 Git 真正意义上的杀手锏是远程仓库。
Git 是分布式版本控制系统同一个 Git 仓库可以分布到不同的机器上。那么是怎么分布的呢最早只有一台机器有一个原始版本库此后别的机器可以“克隆”这个原始版本库而且每台机器的版本库其实都是一样的并没有所谓的主次之分。
在实际工作场景往往是这样的找一台电脑作为“服务器”其他人都从这个服务器仓库“克隆”一份到自己的电脑上并且各自把各自的提交推送到服务器仓库里也从服务器仓库中拉取别人的提交。服务器在这个过程中充当数据转接的角色仅仅作为数据共享的中介。
Git 服务器可以自己搭建也可以使用代码托管平台对于初学 Git 的小伙伴来说搭建自己的服务器不太现实所以直接注册 GitHub 或者 Gitee 的账号使用外部的服务器来托管代码是最实际最高效的选择。前面三小节已经把账号注册完毕并通过 SSH Key 加密连接了现在我们就可以直接构建自己的远程仓库。
4-1. 构建 GitHub 的远程仓库
登录 GitHub 账号先点击右上角的小三角形在下拉菜单中选择New repository此时页面会刷新在Repository name下面的输入框中输入仓库名前面一直用git_test这个仓库名这里要使用一样的。最后点击下面的绿色按钮Create repository创建仓库。要提醒一点就是仓库名不能重复如果你输入了仓库名是自己帐号里已存在的仓库是无法创建成功的。 创建成功会刷新成如下图的界面。 回到本地打开本地仓库git_test的文件夹在这个文件夹下打开 Git Bash粘贴下面的命令上图红框复制的。
git remote add origin https://github.com/zhengxinyu13/git_test.git远程库的名字就是origin这是 Git 默认的叫法也可以改成其它的不过origin这个名字一看就知道是远程库所以建议不改。 然后执行下面的命令
git branch -M main简单解释一下这个命令这是用于将当前仓库的默认分支名称从旧名称如master更改为main。这在许多情况下是为了避免使用具有历史负担的术语比如master/slave或master分支的历史含义。
具体来说git branch -M main 命令执行以下操作
-M 选项表示move或rename。它会重命名分支如果分支已存在则会强制覆盖。main 是新的分支名称。
这个命令将当前仓库的默认分支重命名为 main如果之前存在名为 main 的分支则会覆盖它。 将本地库同步到远程库可以执行下面的命令
git push -u origin main由于远程仓库是空的第一次推送main分支时加上了-u参数Git 不但会把本地的main分支内容推送的远程新的main分支还会把本地的main分支和远程的main分支关联起来在以后的推送或者拉取时就可以简化命令。执行命令后会提示登录 GitHub 的账号点击Sign in with your browser。 然后会自动跳转到浏览器点击Authorize git-ecosystem。 然后输入密码点击Confirm。 如果看到这个界面就表示配置成功。 Git 这边也会提示表示已经成功把本地仓库推送到远程仓库了。 推送成功后可以立刻在 GitHub 页面中看到远程库的内容已经和本地一模一样的文件了。 一旦有了远程仓库本地仓库做出修改需要同步到远程仓库的话就可以通过命令
$ git push origin main这样就可以把本地的main分支的最新修改推送到 GitHub 的远程仓库。
4-2. 构建 Gitee 的远程仓库
Gitee 创建远程仓库会 GitHub 基本上一模一样而且界面是中文的对英语不好的小伙伴很友好操作过程我就不赘述了登录 Gitee 后看图操作。 创建好后在本地仓库的 Git Bash 上执行下图红框的命令。 如果前面已经把本地仓库和 GitHub 的远程仓库连接的话再执行git remote add origin是会失败的如下图 通常情况下Git 不允许你添加同名的远程仓库因为每个远程仓库都应该有一个唯一的名称。
如果你确定要将远程仓库更改为新的地址你可以使用以下命令
git remote set-url origin https://gitee.com/Grayson_Zheng/git_test.git这将更新现有远程仓库 origin 的 URL 为指定的新地址。 不过这么做会使本地仓库与 GitHub 的远程仓库失去连接后面推送只能推到 Gitee 上这就需要后面的克隆仓库来解决了。
执行git push -u origin master时会出错这是因为本地仓库现在的分支名为main而不是master所以改成main之后就可以了。 同样需要登录 Gitee 的账号这一步比 GitHub 简单多了。 执行成功可以看到下图红框的内容。 Gitee 的远程仓库也同步完成。 5. 克隆远程仓库
由于有远程仓库本地仓库即使不小心删除了也可以通过克隆仓库找回。 克隆仓库需要用之前提到的命令git clone后面跟上远程仓库的地址就可以了。其实不管是 GitHub 还是 Gitee 它们给出的地址不止一个实际上 Git 支持多种协议比如 SSH、HTTPS 等。
以克隆git_test这个项目为例GitHub 的操作是先点击绿色按钮Code然后复制链接。 再git clone后面粘贴链接就可以克隆了如下所示
git clone https://github.com/zhengxinyu13/git_test.git而 Gitee 也是差不多的操作在项目页面点击橘色按钮克隆/下载。 弹窗中可以直接复制链接比 GitHub 跟人性化一些的是git clone已经包含在里面了不需要额外再输入。 [!CAUTION] 需要注意的是由于这个仓库虽然分别存放在 GitHub 和 Gitee 上但是仓库的名字是一样如果两个一起克隆就要注意不能放在同一个文件夹下面要分开存放毕竟克隆下来就是一个本地仓库本地仓库本身就是个文件夹同一级目录下是不允许有两个同名文件或文件夹的。 五、分支管理
1. 为什么需要分支
分支是版本管理系统中的重要概念一个版本库中的不同分支互不干扰、完全独立直到分支合并那一天。那么实际工作中分支的作用到底是什么呢 [!NOTE] 假设你准备开发一个新功能但是需要两周的时间才能完成第一周你写了 50% 的代码如果立刻提交由于代码还没写完不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交又存在丢失每天进度的巨大风险。但是有了分支就不需要担心这种事情了。你可以创建一个属于你自己的分支这样团队其他人是看不到的而且还继续在原来的分支上正常工作。你在自己的分支上干活想什么时候提交都可以直到开发完毕后再一次性合并到原来的分支上这样即安全又不影响别人工作。 Git 分支主要是为了实现并行开发和代码管理的灵活性和效率。以下是一些使用分支的主要原因 并行开发: 分支允许团队成员在不影响主代码库的情况下独立地开发新功能或修复错误。每个分支都是代码库的一个拷贝团队成员可以在自己的分支上进行修改然后在开发完成后将其合并回主分支。 功能开发: 每个功能可以在自己的分支上进行开发这样可以保持主分支的稳定性。一旦功能开发完成并通过测试就可以将其合并到主分支中。 错误修复: 当发现主分支中的错误时可以创建一个新分支来修复该错误而不会中断其他正在进行的工作。修复后可以将修复的代码合并回主分支中。 版本管理: 分支可以用来管理不同的版本。例如可以创建一个用于发布的稳定分支以及一个用于持续开发的开发分支。这样可以确保发布版本的稳定性同时继续进行新功能的开发。 代码审查: 分支可以用于进行代码审查。开发人员可以在自己的分支上进行工作并在完成后请求代码审查。这样可以确保代码的质量和一致性。
综上所述分支是 Git 的重要特性可以提高团队协作的效率同时保持代码库的整洁和稳定。但 Git 的分支是与众不同的无论创建、切换还是删除Git 都能快速完成无论你的版本库是 1 个文件还是 1 万个文件。
关于怎么学会使用 Git 的分支这里推荐一个很不错的学习网站Learn Git Branching。
这个网站也是 GitHub 上的一个开源项目pcottle/learnGitBranching有兴趣的可以克隆下来看看。
2. 理解分支
在前面提到的回退版本中可以知道每次提交到版本库 Git 都会把这些提交版本都串成一条时间线这是比较抽象的概念所以可以参考下图 在本地仓库中每一次的修改都会被 Git 记录在这条时间线上这条时间线也是一个分支而且是主分支以前版本的 Git 叫master分支Gitee 仍然保持这个叫法现在版本的 Git 已经开始改名叫main分支了而且 GitHub 也同步了这个叫法。
前面我们还提到有个 HEAD 指针指向最后一个修改其实这不是一个严谨的说法。严格来讲 HEAD 指向的是当前分支更专业的说法是对当前所在分支的符号引用而master或main才是指向提交的节点。一开始时HEAD 只指向master或main而master或main指向一个初始节点这个节点是在仓库初始化之后就有了。之后每一次修改提交都会新增一个节点同时也会使master或main指向新的节点。因此随着版本不断地迭代修改master或main这条分支也会越来越长。 [!NOTE] 从个人角度来讲这块内容比较抽象理解起来会比较困难因为当时我学习 Git 的时候也是这样的状态。后面我把分支类比成数据结构里的链表把分支类比成链表的头指针把修改类比成链表的结点。每次有新的提交就当作有新的结点插入了链表并且让头指针分支指向新结点最新的修改。 3. 分支的创建与合并
前面我们提到了一个场景就是新功能的开发可能需要很多时间而在这段开发的时间里如果功能还没做完就提交可能会影响别人开发如果等到功能开发好后在提交那么中间要是出现电脑宕机等不可抗力的原因导致代码丢失那也得不偿失。那么既不影响别人开发也不影响自己进度的办法就是创建一个自己的分支。
通常我们都会用dev来命名这个分支在 Git 中dev指的是“development”开发的缩写它是一个常见的命名约定用于表示开发阶段的分支用于进行新功能的开发。
假设现在有个现在新建了版本库里面写了初始版本的代码当下如果用 Git 进行版本管理的话可以用下图表示现在情况。 然后对初始版本进行修改后得到了C1版本提交后如下图 这时我需要添加一个新的功能到这个版本里面但是又害怕破环了原来的代码于是我就创建了一个新的分支进行开发。创建新分支并命名为dev的命令如下
git branch dev此时有个需要注意地方那就是 HEAD 指针。此时的 HEAD 还是指向主分支如果我这时候修改代码并提交了这个提交记录还是在主分支上那我创建的新分支就没有任何意义了。因此在创建新分支之后要及时把 HEAD 指向新的分支之后的提交记录才会记在新分支上。求换分支的命令如下
git checkout dev当然如果想在创建新分支后自动切换到新分支可以用下面这个命令
git checkout -b dev这样就不需要用到git branch命令了可以省一条命令。
这时我写好了一部分的新功能虽然没有完成全部功能但是为了保险起见我提交了这次的修改于是有了C2版本的代码并且提交在了dev分支上。 接着我有连续开发C3版本和C4版本坏消息是虽然我完成了新功能的开发但是经过测试都没能达到预期同时也影响到了原本好的功能。好消息是主分支的代码还能正常运行。 终于功夫不负有心人当开发到C5版本时终于实现了功能测试也没有发现 Bug于是我提交了C5版本。 然后再就把这次提交合入到主分支中去这个过程需要将 HEAD 切回主分支再进行合并。切换回主分支的命令是git checkout后面接上主分支名如下
git checkout main切换主分支后再用git merge命令合并dev分支具体如下
git merge dev对于dev分支来说它看到了几个版本的迭代但是对于主分支来说只是C1到C5的迭代。合并之后就可以放心的将dev分支删除了。不过不需要担心曾经在dev分支提交的各种记录会被删除即使分支被删除这个分支上的提交记录会被保留一段时间直到 Git 触发了垃圾回收并删除了这些无用的提交。删除分支的命令如下
git branch -d dev因为创建、合并和删除分支非常快所以 Git 鼓励使用分支完成各种任务合并后再删掉分支这和直接在主分支上工作效果是一样的但过程更安全。
4. 冲突解决
只要是团队开发项目代码上的冲突是在所难免的毕竟团队成员基本都在各自的分支里开发那么合并分支就不可能一帆风顺这时就要解决各种冲突了。
在 Git 中所谓的冲突指的是当合并操作merge或重置操作reset尝试合并两个不同的修改时发生的情况。这种情况会导致 Git 无法自动解决修改之间的矛盾需要人工介入解决。
常见的情况是当你试图合并两个不同的分支但这两个分支上的相同文件被修改了且这些修改彼此之间存在冲突。例如如果两个分支都修改了同一个文件的同一行代码Git 就无法自动确定应该保留哪个版本的修改因此会产生冲突。
当Git发现冲突时会在受影响的文件中插入特殊标记来标识冲突的部分例如 HEAD
这是当前分支HEAD的修改这是另一个分支的修改other_branch冲突标记之间的内容就是冲突的部分。在解决冲突时你需要手动编辑这些文件选择要保留哪些修改然后将冲突标记删除以及合并双方的修改。一旦解决了所有冲突你就可以继续完成合并操作。
下面举一个简单的例子演示一下如何解决开发过程中遇到的版本库冲突的问题。
首先在现有的 Git 版本库创建一个新的分支feature。 前面没有提到这里补充一下查看本地仓库有多少分支可以用git branch命令Git 会列出所有的本地分支其中字体为绿色且前面带有星号的分支就是当前被 HEAD 指向的分支。
此时我修改了 README.md 文件并保存提交。 此时我切换回主分支同样修改了 README.md 文件并保存提交。修改的内容和上图一样只是换了一个符号。 好了现在两个分支都有对同一个文件的提交如果这时候合并分支就会有冲突Git 也会有提示合并存在冲突需要手动解决然后重新提交。 可以用git status命令来查看存在冲突的文件。 这时我们再打开 README.md 文件可以看到 Git 标记出不同分支的内容。 修改成下图所示的内容后保存这个要修改成哪个分支的内容全看实际的情况我这里是随便选的。 解决冲突后使用 git add 命令将解决冲突的文件标记为已解决再用 git merge --continue来继续合并进程。 用带参数的git log也可以看到分支的合并情况别慌这些命令我也没记住以前靠百度现在靠 ChatGPT。
git log --graph --prettyoneline --abbrev-commit5. BUG 分支
软件开发中bug 就像家常便饭一样。有了 bug 就需要修复在 Git 中每个bug都可以通过一个新的临时分支来修复修复后合并分支然后将临时分支删除。
假设我现在接到一个修复代号为 101 的 bug 的任务时而且这个 bug 相对比较紧急所以我需要放下当前的工作优先去处理这个 bug那么我就会创建一个名为 issue-101 的分支来修复它。不过正在dev分支上进行的工作还没有提交只是添加到了暂存区而已。 此时我还不能提交毕竟当前的进度还未完成没办法提交。同时代号 101 的 bug 又需要在今天解决那要怎么办呢
在前面的 Git 工作流程中有一个贮藏区stash一直没提到现在就是它发挥作用的时候了。贮藏区可以把当前的工作现场“贮藏”起来等以后需要恢复工作现场时再把工作现场还原到“贮藏”前进而可以继续进行未完成的工作。
现在用git status查看版本库的状态还存在未提交的内容。 用git stash命令贮藏当前的工作状态用git status查看版本库的状态可以看到工作区已经 clean 了因此可以放心地创建分支来修复 bug 了。 这时就要注意了先要确定以哪个分支的版本修复 bug毕竟现在的 HEAD 指针还指向dev分支而且dev分支上可能有很多提交是不完整的这时就需要切换回主分支以主分支的最后一个提交的版本为基准去创建新的 bug 分支。 分支创建好后就可以开始修复 bug 了。修复好后提交修改切回主分支合并 bug 分支最后删除 bug 分支。 这时候再回到dev分支干活但是工作区是干干净净的需要去贮藏区恢复后才能继续。 用git stash list命令可以查看当前贮藏区的所有贮藏记录目前看来只有一条记录。 想要恢复成贮藏之前的状态有两种情况 恢复贮藏前的工作状态不删除贮藏区的贮藏记录命令如下 git stash apply后续想要删除要用git stash drop而且还要指定删除哪条记录不指定就是删除最新的指定的话命令如下stash{index}里面的index是记录编号如上图的stash{0}。 git stash drop stash{index}恢复贮藏前的工作状态同时删除贮藏区的贮藏记录命令如下 git stash pop我这里直接用git stash pop不需要用git status恢复的时候会直接显示版本库的状态。 6. 分支管理策略
在合并分支时可以注意到有个“Fast-forward”的字样这是 Git 中的一种合并模式。在 Fast-forward 模式快进合并下Git 可以通过简单地将目标分支指向合并的分支的最新提交来完成合并而无需创建新的合并提交。 在这种模式下Git 只需将目标分支指向合并分支的最新提交而不会创建新的提交来记录合并的过程。快进合并的好处是可以保持提交历史的线性和简洁因为不会创建额外的合并提交。这在处理简单的合并情况时非常方便和高效。但这种模式有个缺点那就是删除分支后会丢掉分支信息。
所以有时候快进合并可能不太适用比如当我想要保留合并操作的记录或者在合并过程中需要解决冲突时。在这种情况下可以使用--no-ff选项来禁用快进合并强制 Git 创建一个新的合并提交。这样可以更清晰地记录分支的合并历史以及解决合并冲突所做的修改。
例如我现在在dev分支提交了一笔代码准备合并dev分支。如果想要保留提交信息就要强制禁用 Fast forward 模式Git 就会在 merge 时生成一个新的 commit那么从分支历史上就可以看出分支信息了。 在dev分支提交后切回主分支然后合并dev分支时带上--no-ff和合并信息命令如下双引号是合并信息
git merge --no-ff -m merge with no-ff dev合并后看看分支历史可以明显看到前面演示的分支合并和这次的禁用快进合并的分支合并有着明显的信息差别禁用快进合并的分支合并操作会记录下这次的提交是从dev分支合并过来的。 在实际开发中分支管理是一个至关重要的实践它可以帮助团队更好地组织和协作。以下是一些基本原则和最佳实践可以进行有效的分支管理
主分支保持稳定性 主分支通常是 master 或 main应该保持稳定反映了生产环境中的代码状态。在主分支上应该只合并已经经过测试和验证的代码。使用特性分支 对于新功能或修复问题应该创建专门的特性分支。每个特性分支都应该专注于解决一个特定的问题或实现一个特定的功能。这样可以使得代码更容易管理和测试。及时合并主分支变更 团队成员应该经常将主分支上的最新变更合并到自己的特性分支中以避免特性分支与主分支之间的差异过大导致合并冲突难以解决。小步快走 尽可能频繁地提交和合并代码变更以减小每次合并的规模。这有助于降低合并冲突的发生率并使得问题的定位和解决更加容易。Code Review 所有代码变更都应该经过代码审查。通过 Code Review 可以发现潜在的问题并提高代码质量。通常在代码审查通过后才允许将代码合并到主分支。避免直接推送到主分支 避免直接推送代码到主分支除非是紧急修复或小的修改。这样可以确保所有的代码变更都经过了适当的审查和测试。定期清理不需要的分支 定期清理已经完成的特性分支或不再需要的分支以保持仓库的清洁和可管理性。合并策略选择 根据团队的需求和项目的特点选择合适的合并策略比如快进合并、非快进合并、rebase 等。文档化 在团队中建立清晰的分支管理规范和流程并确保所有成员了解和遵守这些规范。文档化可以帮助新成员快速上手并减少误解和混乱。持续改进 不断地评估和改进分支管理流程以适应项目的发展和团队的需求。及时调整流程可以帮助团队更加高效地进行开发工作。
有了这些原则实际的团队合作的分支其实就像下图这样很多大型项目上万条分支都是正常的。 7. 特性分支
做软件开发总有无穷无尽的新功能要添加从上图也可以知道新功能一般都会在dev分支上的基础上构建一个feature分支来开发。这也是多数团队默认的习惯还是那个原则不能让实验性代码破坏主分支。
假设我现在接到了 Leader 安排的新任务要我开发一个新功能任务的代号为 feat-013。
于是我在dev分支的基础上创建了feat-013分支。 过程很顺利我提交了新功能并切回了dev分支准备把性能合并进来。 但就在这时Leader 跟我说预算不足我负责的功能被砍掉了。然白干了但是这个包含机密资料的分支还是必须就地销毁。但是用git branch -d会发现删不掉这个分支因为这个分支还没有合并。不过 Git 也会提示可以用git branch -D来删除分支。 8. 多人协作
从远程仓库克隆时Git 会自动把本地的主分支和远程的主分支对应起来了并且远程仓库的默认名称是origin。要查看远程库的信息用git remote命令或者git remote -v命令。 上图显示了可以抓取和推送的origin地址。如果没有推送权限就看不到push的地址。
8-1. 推送分支
所谓推送分支就是把该分支上的所有本地提交推送到远程库推送时要指定本地分支。在 Git 中要推送分支到远程仓库可以使用 git push 命令。如下图 在推送前要确认推送的分支是哪一个比如要推送主分支那就先切换到主分支上在执行git push命令后面的origin mian表示远程仓库的main分支也就是我把本地主分支的提交合并到远程仓库的主分支上。
同理推送其他分支也是类似的操作比如dev分支先切分支再推送。 使用git branch -a可以查看本地和远程仓库的所有分支信息。 不过要注意一点不是本地所有的分支和提交都要往远程仓库推送。首先主分支基本都是时刻与远程同步。其次是dev分支团队所有成员都想要在上面工作所以也要与远程同步。最后类似修复 bug 用的临时分支和用于开发新功能feature分支就看情况同步了。一般来说本地用于修复 bug 的分支是不需要推送的除非 Leader 要看你的进度而feature分支就取决于是单独开发新功能还是团队协作开发了。
8-2. 抓取分支
多人协作时团队成员都会往远程仓库推送各自的修改以及向远程仓库抓取分支。不过要注意一点抓取分支和克隆是 Git 中的两个不同的操作。克隆是指从远程仓库复制整个仓库的操作。当使用git clone命令时Git 会复制远程仓库的所有文件、提交历史、分支等信息到本地并创建一个与远程仓库相同的本地仓库副本。
假设我现在所处的团队中我和另一个小伙伴被安排一起开发一个功能我们一起克隆了同一个远程仓库默认情况下都是克隆远程仓库的主分支到本地。 但有时候开发需要克隆的是远程仓库的dev分支如上图仓库远程仓库有两个分支那么指定远程dev分支可以用下面的命令
git clone -b dev https://github.com/zhengxinyu13/git_test.git如果一开始克隆时没有加上-b 选项和指定分支克隆的仓库则是默认克隆主分支这时也不用着急把本地仓库删掉可以创建远程 origin 的dev分支到本地如下命令
git checkout -b dev origin/dev现在我和小伙伴都用相同分支克隆下来的版本做开发期间他把对某个文件的修改推送到origin/dev分支而碰巧我也对同个文件做出了修改如果我也推送到origin/dev分支就会推送失败。因为小伙伴最新的提交和我试图推送的提交有冲突解决办法也很简单Git 也会提示用git pull命令把最新的提交从origin/dev分支抓下来然后在本地合并解决冲突后再推送。具体命令如下
git pull origin dev这就是多人协作的工作模式一旦熟悉了就非常简单。
9. 删除远程分支
要删除远程仓库中的分支可以使用git push命令来向远程仓库推送一个空的分支实际上相当于删除远程分支。以下是具体步骤
git push origin --delete branch_name其中origin是远程仓库的名称branch_name是要删除的分支的名称。
例如如果你要删除远程仓库origin中的dev分支可以使用以下命令
git push origin --delete dev执行这个命令后dev分支将会被从远程仓库origin中删除。
六、标签管理
1. 什么是标签
Git 标签Tag是用来标记某个特定提交的通常用于标识版本发布、重要里程碑等。Git 的标签是版本库的快照但其实它就是指向某个 commit ID 的指针跟分支会像但是分支可以移动标签不能移动。 [!TIP] 那么为什么 Git 已经有 commit ID却还要引入 tag 呢 同样来假设一个场景某个软件版本发布日Leader 跟我说“请把上周一的那个版本打包发布commit ID 是 6a5819e…”我心想“一串乱七八糟的数字不好找” 但是同样的场景Leader 跟我说“请把上周一的那个版本打包发布版本号是 v1.2。”我就会这么想“按照 tag v1.2 查找 commit ID 就行了。” 可以这么理解标签就是一个让人容易记住且有意义的名字。
2. 创建标签
在 Git 中打标签非常简单先切换到需要打标签的分支上然后用git tag加上标签名称既可以了。 git tag可以查看所有的标签加上标签名则是给最新的提交打上标签。同时用git log查看也可以看到标签跟提交绑定在一起。 由于默认是最新的提交打标签如果给之前的提交打标签就需要找到对应的 commit ID 了。例如我要给上图所示的“fix issues-101”打个标签那我就先复制一下对应的 commit ID再用git tag打上标签。上图的 commit ID 太长了可以用下面的命令获得简短 SHA-1 标识符abbreviated commit
git log --prettyoneline --abbrev-commit再用git tag可以查看所有的标签可以看多了个v0.9的标签。注意标签不是按时间顺序列出而是按字母排序的。 标签还可以显示提交信息用git show命令加上标签名就可以了。 而且创建标签时还可以顺带写上一些描述说明用-a指定标签名-m指定描述说明。 3. 管理标签
只要是人做的事都有可能犯错因此标签也有可能会打错但是没关系Git 是可以删除标签的同样是git tag命令加上-d选项就可以了。 目前所有的标签都只是在本地仓库不会自动推送到远程仓库。如果要推送某个标签到远程仓库可以用git push origin tagname命令。 如果要一次性推送所有的标签可以只用git push origin --tags。 当把所有的标签都推送到远程仓库发现有一个标签本来要删除一时疏忽给忘了那就需要先在本地把标签删了然后用下面的命令删除远程仓库的标签其中tag_name是标签名。
git push origin :refs/tags/tag_name这个命令的语法是通过将一个空的本地分支推送到远程仓库的标签位置来删除标签。如果要直接删除远程仓库中的标签可以使用以下命令
git push origin --delete tag_name七、Git 自定义
1. 文件忽略
日常开发中工作区可能有些文件是不需要提交的例如一些测试功能时产生的 log或者是数据库密码的配置文件等等。这些文件需要放在工作区但是又不能提交每次输入git status时又会显示 Untracked files着实让强迫症患者抓狂。
好在 Torvalds 也是个强迫症晚期在开发 Git 的时候也想到了这一点可以通过.gitignore文件来指定需要忽略的文件和文件夹。.gitignore文件通常位于项目的根目录中用于告诉 Git 哪些文件和文件夹不应该被纳入版本控制。
以下是一些常见的 .gitignore 文件的用法 忽略特定文件或文件夹 如果想忽略特定的文件或文件夹只需在 .gitignore 文件中添加它们的名称即可。例如 file_to_ignore.txt
folder_to_ignore/使用通配符 可以使用通配符来匹配一类文件。例如使用*可以匹配任意字符序列使用?可以匹配单个字符。例如 *.log # 忽略所有 .log 文件
*.tmp # 忽略所有 .tmp 文件
secret_* # 忽略以 secret_ 开头的文件注释 可以在.gitignore文件中使用#符号添加注释。例如 # 这是一个注释忽略整个文件夹 如果想忽略整个文件夹及其所有内容只需在.gitignore文件中添加文件夹的名称即可。例如 node_modules/ # 忽略 node_modules 文件夹及其所有内容[!CAUTION] .gitignore文件中的每一行都描述了一个忽略模式。模式可以是文件名、文件路径或者通配符Git 会根据这些模式来确定哪些文件应该被忽略。 不过现在也不需要从头写.gitignore文件这个 GitHub 上的项目《github/gitignore:》点击跳转为我们总结了几乎适用于所有软件开发的.gitignore文件可以根据自己开发项目所用的编程语言选择例如我常用 C/C 开发我就下载对应的文件夹。 那么可能会有这样的问题那就是有时候可能需要提交某个文件刚好是被忽略的那要怎么添加呢
如果打开.gitignore文件重新修改就很麻烦也不高效所以在把工作区提交到暂存区时加上-f选项表示强制提交。具体如下
git add -f file_name2. 配置别名
在 Git 中你可以通过配置别名来创建自定义的命令或简化常用的 Git 命令。可以在.gitconfig文件中设置别名也可以使用git config命令来设置。
以下是一些常见的 Git 别名配置示例 使用git config命令设置别名 git config --global alias.co checkout这个命令会创建一个名为 co 的别名用于执行 git checkout 命令。 在.gitconfig文件中设置别名 打开 .gitconfig 文件并添加类似以下内容的配置 [alias]co checkout这个配置会创建一个名为 co 的别名用于执行 git checkout 命令。 设置带参数的别名 git config --global alias.br branch -a这个命令会创建一个名为 br 的别名用于执行 git branch -a 命令。 设置自定义命令别名 git config --global alias.history log --prettyformat:%h %ad | %s%d [%an] --graph --dateshort这个命令会创建一个名为history的别名用于执行自定义的git log命令可以显示更简洁的提交历史。
设置好别名后你可以直接使用别名来执行相应的 Git 命令这样可以提高效率并减少输入量。 3. 配置文件
配置 Git 的时候加上--global是针对当前用户起作用的如果不加那只针对当前的仓库起作用。每个仓库本地的配置文件的位置都放在本仓库的.git/config文件中。 Git 的配置文件包括三个级别系统级别、全局级别和仓库级别。它们分别存储在不同的位置作用范围也不同。 系统级别配置文件 这个配置文件位于 Git 安装目录下的etc/gitconfig文件中。它包含了对系统上所有用户都适用的配置通常由系统管理员进行管理。你可以使用git config --system命令来修改系统级别的配置但可能需要管理员权限。 全局级别配置文件 这个配置文件位于用户的主目录下的.gitconfig或者.config/git/config文件中取决于操作系统和 Git 版本。它包含了对当前用户所有仓库都适用的配置。你可以使用git config --global命令来修改全局级别的配置。 仓库级别配置文件 这个配置文件位于 Git 仓库的根目录下的.git/config文件中。它包含了对当前仓库的特定配置。你可以使用git config命令来修改仓库级别的配置不需要任何特殊权限。
在配置文件中你可以设置一些 Git 的行为选项、别名、用户信息等。以下是一个简单的示例
[user]name Your Nameemail your.emailexample.com[alias]co checkoutci commitst status[core]editor nano这个配置文件设置了用户信息、一些常用的别名以及指定了默认的文本编辑器。你可以根据需要在配置文件中添加、修改或删除配置项。
要查看当前的 Git 配置可以使用git config --list命令它会列出当前生效的所有配置。 附录
1. 关于 commit 提交信息规范
编写规范的 Git commit 信息对于团队协作和项目维护非常重要。以下是一个适用于嵌入式开发的 Git commit 信息规范示例
type(scope): subjectdescriptionfootertype同样是描述 commit 的类型可能包括 feat新功能fix修复问题docs文档修改refactor重构代码test增加或修改测试chore构建过程或辅助工具的变动 scope描述 commit 影响的范围可以是一个模块、驱动、功能名字等。subject简短描述 commit 的目的用一句话概括。description详细描述 commit 的内容。对于嵌入式系统可能需要说明与硬件相关的修改、驱动的更新、性能优化等。footer一些附加信息比如关联的 issue、版本号、特定的硬件平台等。
例如
feat(sensor): implement driver for temperature sensorAdded driver for XYZ temperature sensor to handle temperature readings.- Implemented initialization routine
- Added functions to read temperature in Celsius and Fahrenheit
- Tested on hardware version 2.1Fixes #321在嵌入式开发中还可能会有一些特定的标签或者约定比如硬件版本、特定的编译器或工具链版本等信息需要根据项目实际情况进行调整和添加。
参考资料
Git教程 - 廖雪峰的官方网站 (liaoxuefeng.com)
这个配置会创建一个名为 co 的别名用于执行 git checkout 命令。 设置带参数的别名 git config --global alias.br branch -a这个命令会创建一个名为 br 的别名用于执行 git branch -a 命令。 设置自定义命令别名 git config --global alias.history log --prettyformat:%h %ad | %s%d [%an] --graph --dateshort这个命令会创建一个名为history的别名用于执行自定义的git log命令可以显示更简洁的提交历史。
设置好别名后你可以直接使用别名来执行相应的 Git 命令这样可以提高效率并减少输入量。
[外链图片转存中…(img-2u3qc6K0-1713703279247)]
3. 配置文件
配置 Git 的时候加上--global是针对当前用户起作用的如果不加那只针对当前的仓库起作用。每个仓库本地的配置文件的位置都放在本仓库的.git/config文件中。
[外链图片转存中…(img-oEZbJta0-1713703279247)]
Git 的配置文件包括三个级别系统级别、全局级别和仓库级别。它们分别存储在不同的位置作用范围也不同。 系统级别配置文件 这个配置文件位于 Git 安装目录下的etc/gitconfig文件中。它包含了对系统上所有用户都适用的配置通常由系统管理员进行管理。你可以使用git config --system命令来修改系统级别的配置但可能需要管理员权限。 全局级别配置文件 这个配置文件位于用户的主目录下的.gitconfig或者.config/git/config文件中取决于操作系统和 Git 版本。它包含了对当前用户所有仓库都适用的配置。你可以使用git config --global命令来修改全局级别的配置。 仓库级别配置文件 这个配置文件位于 Git 仓库的根目录下的.git/config文件中。它包含了对当前仓库的特定配置。你可以使用git config命令来修改仓库级别的配置不需要任何特殊权限。
在配置文件中你可以设置一些 Git 的行为选项、别名、用户信息等。以下是一个简单的示例
[user]name Your Nameemail your.emailexample.com[alias]co checkoutci commitst status[core]editor nano这个配置文件设置了用户信息、一些常用的别名以及指定了默认的文本编辑器。你可以根据需要在配置文件中添加、修改或删除配置项。
要查看当前的 Git 配置可以使用git config --list命令它会列出当前生效的所有配置。 参考资料
Git教程 - 廖雪峰的官方网站 (liaoxuefeng.com)
Git - Book (git-scm.com)