Git基础
Git版本控制
一、版本控制
(一)什么是版本控制?
版本控制是一种记录和管理计算机文件或项目的变化历史的系统。它允许开发人员或团队跟踪文件的每次修改、查看更改内容、比较不同版本之间的差异,并在需要时还原到之前的特定状态。版本控制对于软件开发和其他类似项目的管理非常重要,因为它提供了许多优势和便利。
主要的版本控制系统有两种类型:集中式版本控制系统(Centralized Version Control System,CVCS)和分布式版本控制系统(Distributed Version Control System,DVCS)。
集中式版本控制系统(CVCS):
在CVCS中,所有文件的版本历史都保存在中央服务器上。开发人员通过连接到中央服务器来检出最新版本的文件,并将自己的更改提交到中央服务器。CVCS的缺点是,如果中央服务器出现故障或网络连接问题,那么开发人员将无法提交更改或获取最新的代码。此外,对于较大的团队,由于所有开发人员都使用中央服务器,可能会造成服务器性能瓶颈。分布式版本控制系统(DVCS):
在DVCS中,每个开发人员都有一个完整的代码仓库,包含完整的项目历史。开发人员可以在本地进行提交、回滚、创建分支等操作,而不需要实时连接到中央服务器。这使得团队成员可以在离线状态下工作,并且可以更加灵活地协作。当开发人员准备好时,他们可以将他们的更改推送(push)到中央服务器,或者从中央服务器拉取(pull)其他人的更改。
版本控制系统的主要优势包括:
- 更好的协作:团队成员可以同时在同一个项目上工作,不会干扰彼此的进度,也能更容易地合并不同人员的修改。
- 历史记录:每个版本都被记录下来,可以查看文件的完整修改历史,包括何时做了哪些更改。
- 版本回退:如果某次修改引入了问题,可以轻松回退到之前的稳定版本。
- 分支管理:可以创建多个分支,每个分支可用于不同的功能开发或问题修复,而不会相互干扰。
- 远程备份:中央服务器提供了对代码的远程备份,减少了数据丢失的风险。
版本控制系统的使用不仅局限于软件开发,它还可以在各种其他场景中应用,如文档管理、项目管理、设计文件等。
(二)为什么需要版本控制?
以论文写作为例,在没有版本控制工具前,我们往往是通过每次修改版本都保留一个文件的方式来管理论文。通过多个文件来确定各个版本,不仅会占用更多的空间,而且也不利于我们对比版本间的差异。此外,我们也无法和他人协作。
使用版本控制工具,我们在论文写作中可以实现:
版本历史记录: 版本控制工具记录了每次对论文进行的修改和更新,包括添加、删除、编辑等。这使得作者可以随时查看论文的历史版本,了解每个修改的内容和时间,方便跟踪研究进展和撰写过程。
版本回退和比较: 作者可以轻松地回退到之前的版本,如果当前的修改出现问题或需要恢复之前的内容,这在不丢失任何数据的情况下进行。此外,版本控制工具允许作者比较不同版本之间的差异,帮助他们了解具体修改的细节和影响。
分支管理: 版本控制工具允许作者创建多个分支,每个分支可以用于不同的实验、讨论或章节撰写。这样可以在不影响主线论文的情况下进行尝试和修改,保持主线内容的稳定性。
协作与审阅: 版本控制工具使得多位作者可以并行地对论文进行编辑和审阅。每位作者都可以在自己的本地副本上工作,然后将更改推送到共享的远程仓库。这样,团队成员可以实时了解其他人的修改,并且可以更好地合并和处理冲突。
备份和安全性: 版本控制工具提供了对论文内容的远程备份,减少了数据丢失的风险。即使本地计算机出现故障,作者的论文仍然安全存储在远程服务器上。
跨平台支持: 大多数版本控制工具都支持跨平台,这意味着作者可以在不同的操作系统上(如Windows、Mac、Linux)使用同一个版本控制系统,并轻松地共享和访问论文内容。
注释和讨论: 版本控制工具通常支持注释和讨论功能,团队成员可以在特定的修改或提交上留下评论,以便进行讨论、提供反馈或交流想法。
总体而言,版本控制工具为论文写作带来了更好的协作、追踪和管理的能力,提高了论文写作的效率,并保障了论文内容的安全性和稳定性。这对于研究团队、学生、教师以及其他合作写作项目的人员来说,都是非常有价值的工具。
(三)常用版本控制工具?
常用的版本控制方法和工具主要有以下几种:
集中式版本控制系统(CVCS):
- CVS (Concurrent Versions System):是最早流行的集中式版本控制系统之一,但目前已逐渐被更先进的版本控制系统所取代。
- Subversion (SVN):一种更现代和流行的集中式版本控制系统,提供了更多功能和改进,仍然在一些项目中广泛使用。
分布式版本控制系统(DVCS):
- Git:目前最流行和广泛使用的分布式版本控制系统。Git 是由 Linus Torvalds 创造的,它非常快速、强大,并得到了全球开发者社区的支持。
- Mercurial:另一种流行的分布式版本控制系统,与 Git 类似,但在一些特定场景下可能具有不同的优势。
集成开发环境(IDE)内置版本控制:
- 一些集成开发环境(如Visual Studio、Eclipse、IntelliJ IDEA等)具有内置的版本控制集成,通常支持 Git 和 SVN 等常见的版本控制工具。
在线托管平台的版本控制:
- GitHub:一个基于 Git 的在线代码托管平台,广泛用于开源项目、团队协作以及个人项目管理。
- GitLab:类似于 GitHub 的另一个在线代码托管平台,提供类似的功能,并且可以自行搭建在私有服务器上。
- Bitbucket:提供 Git 和 Mercurial 支持的在线代码托管平台,通常用于私有项目。
这些版本控制方法和工具在软件开发、文档管理、论文写作、项目协作等领域广泛使用。选择哪种版本控制方法和工具取决于项目的需求、团队的喜好和技术要求。Git 是目前最受欢迎和推荐的版本控制系统,它为开发者提供了强大的功能和灵活性,并在开源社区中得到广泛支持。
二、 Git
(一)什么是 Git?
Git是一种分布式版本控制系统,它用于跟踪计算机文件的变化和协调多人在同一个项目上的协作。版本控制是在软件开发和其他类似项目中非常重要的概念,因为它允许开发人员或团队记录每个文件的更改历史并管理不同版本之间的差异。
Git由Linus Torvalds于2005年创造,最初是为了更好地管理Linux内核的开发而设计的。现在,Git已经成为世界上最流行和广泛使用的版本控制系统之一,被广泛应用于软件开发和其他项目管理中。
Git的主要特点包括:
分布式:与传统的集中式版本控制系统不同,Git是一种分布式系统,每个开发人员都有一个完整的代码仓库,包含完整的历史记录。这使得团队成员可以在离线状态下工作,并可以更容易地进行协作。
分支管理:Git非常擅长管理分支,开发人员可以轻松地创建、合并和删除分支,这为同时处理多个功能或修复不同问题提供了便利。
快速和高效:Git被设计成非常快速和高效,使开发人员可以迅速完成各种操作,如提交、分支切换和合并等。
完整的历史记录:Git记录每个文件的完整历史记录,开发人员可以查看文件的每个版本、每次修改和对应的作者信息。
数据完整性:Git使用哈希值来保证数据的完整性,这意味着一旦数据被提交,就会被永久地记录和保留。
开放源代码:Git是开源软件,任何人都可以查看其源代码,并可以根据需要进行自定义修改。
使用Git,开发人员可以更加灵活地协作,管理项目,并追踪代码变化,从而帮助团队更加高效地进行软件开发或其他类型的项目管理。
(二)Git 工作原理
Git 大致分为4个板块:
- 工作目录:存放我们正在写的代码(当我们新版本开发完成之后,就可以进行新版本的提交)
- 暂存区:暂时保存待提交的内容(新版本提交后会存放到本地仓库)
- 本地仓库:位于我们电脑上的一个版本控制仓库(存放的就是当前项目各个版本代码的增删信息)
- 远程仓库:位于服务器上的版本控制仓库(服务器上的版本信息可以由本地仓库推送上去,也可以从服务器抓取到本地仓库)
它是一个分布式的控制系统,因此一般情况下我们每个人的电脑上都有一个本地仓库,由大家共同向远程仓库去推送版本迭代信息。
通过这一系列操作,我们就可以实现每开发完一个版本或是一个功能,就提交一次新版本,这样,我们就可以很好地控制项目的版本迭代,想回退到之前的版本随时都可以回退,想查看新版本添加或是删除了什么代码,随时都可以查看。
(三)安装 Git
首先请前往 Git 官网去下载最新的安装包:https://git-scm.com/
安装完成后,需要设定用户名和邮箱来区分不同的用户:
1 | git config --global user.name "Ang Lee" |
(四)基本命令介绍
- 创建本地仓库
我们可以将任意一个文件夹作为一个本地仓库,输入:
1 | git init |
输入后,会自动生成一个.git
目录,注意这个目录是一个隐藏目录,而当前目录就是我们的工作目录。
创建成功后,我们可以查看一下当前的一个状态,输入:
1 | git status |
如果已经成功配置为Git本地仓库,那么输入后可以看到:
1 | On branch master |
这表示我们还没有向仓库中提交任何内容,也就是一个空的状态。
- 添加和提交
接着我们来看看,如何使用git来管理我们文档的版本,我们创建一个文本文档,随便写入一点内容,接着输入:
1 | git status |
我们会得到如下提示:
1 | Untracked files: |
其中Untracked files是未追踪文件的意思,也就是说,如果一个文件处于未追踪状态,那么git不会记录它的变化,始终将其当做一个新创建的文件,这里我们将其添加到暂存区,那么它会自动变为被追踪状态:
1 | git add hello.txt #也可以 add . 一次性添加目录下所有的 |
再次查看当前状态:
1 | Changes to be committed: |
现在文件名称的颜色变成了绿色,并且是处于Changes to be committed下面,因此,我们的hello.txt现在已经被添加到暂存区了。
接着我们来尝试将其提交到Git本地仓库中,注意需要输入提交的描述以便后续查看,比如你这次提交修改了或是新增了哪些内容:
1 | git commit -m 'Hello World' |
注:如果是修改了文件的内容(不是增删文件),可以用以下一行命令完成添加到暂存区以及提交到本地仓库中:
1 | git commit -a -m 'Modify x' |
接着我们可以查看我们的提交记录:
1 | git log |
我们还可以查看最近一次变更的详细内容:
1 | git show [也可以加上commit ID查看指定的提交记录] |
再次查看当前状态,已经是清空状态了:
1 | On branch master |
接着我们可以尝试修改一下我们的文本文档,由于当前文件已经是被追踪状态,那么git会去跟踪它的变化,如果说文件发生了修改,那么我们再次查看状态会得到下面的结果:
1 | Changes not staged for commit: |
也就是说现在此文件是处于已修改状态,我们如果修改好了,就可以提交我们的新版本到本地仓库中:
1 | git add . |
接着我们来查询一下提交记录,可以看到一共有两次提交记录。
我们可以创建一个.gitignore
文件来确定一个文件忽略列表,如果忽略列表中的文件存在且不是被追踪状态,那么git不会对其进行任何检查:
1 | # 这样就会匹配所有以txt结尾的文件 |
创建后,我们来看看是否还会检测到我们忽略的文件。
- 回滚
当我们想要回退到过去的版本时,就可以执行回滚操作,执行后,可以将工作空间的内容恢复到指定提交的状态:
1 | git reset --hard commitID |
执行后,会直接重置为那个时候的状态。再次查看提交日志,我们发现之后的日志全部消失了。
那么要是现在我又想回去呢?我们可以通过查看所有分支的所有操作记录:
1 | git reflog |
这样就能找到之前的commitID,再次重置即可。
- 分支
分支就像我们树上的一个树枝一样,它们可能一开始的时候是同一根树枝,但是长着长着就开始分道扬镳了,这就是分支。我们的代码也是这样,可能一开始写基础功能的时候使用的是单个分支,但是某一天我们希望基于这些基础的功能,把我们的项目做成两个不同方向的项目,比如一个方向做Web网站,另一个方向做游戏服务端。
因此,我们可以在一个主干上分出N个分支,分别对多个分支的代码进行维护。
- 创建分支
我们可以通过以下命令来查看当前仓库中存在的分支:
1 | git branch |
我们发现,默认情况下是有一个master分支的,并且我们使用的也是master分支,一般情况下master分支都是正式版本的更新,而其他分支一般是开发中才频繁更新的。我们接着来基于当前分支创建一个新的分支:
1 | git branch test |
现在我们修改一下文件,提交,再查看一下提交日志:
1 | git commit -a -m 'branch master commit' |
通过添加-a来自动将未放入暂存区的已修改文件放入暂存区并执行提交操作。查看日志,我们发现现在我们的提交只生效于master分支,而新创建的分支并没有发生修改。
我们将分支切换到另一个分支:
1 | git checkout test |
我们会发现,文件变成了此分支创建的时的状态,也就是说,在不同分支下我们的文件内容是相互隔离的。
我们现在再来提交一次变更,会发现它只生效在test分支上。我们可以看看当前的分支状态:
1 | git log --all --graph |
- 合并分支
我们也可以将两个分支更新的内容最终合并到同一个分支上,我们先切换回主分支:
1 | git checkout master |
接着使用分支合并命令:
1 | git merge test |
会得到如下提示:
1 | Auto-merging hello.txt |
在合并过程中产生了冲突,因为两个分支都对hello.txt文件进行了修改,那么现在要合并在一起,到底保留谁的hello文件呢?
我们可以查看一下是哪里发生了冲突:
1 | git diff |
因此,现在我们将master分支的版本回退到修改hello.txt之前或是直接修改为最新版本的内容,这样就不会有冲突了,接着再执行一次合并操作,现在两个分支成功合并为同一个分支。
- 变基分支
除了直接合并分支以外,我们还可以进行变基操作,它跟合并不同,合并是分支回到主干的过程,而变基是直接修改分支开始的位置,比如我们希望将yyds变基到master上,那么yyds会将分支起点移动到master最后一次提交位置:
1 | git rebase master |
变基后,yyds分支相当于同步了此前master分支的全部提交。
- 优选
我们还可以选择其将他分支上的提交作用于当前分支上,这种操作称为cherrypick:
1 | git cherry-pick <commit id>:单独合并一个提交 |
这里我们在master分支上创建一个新的文件,提交此次更新,接着通过cherry-pick的方式将此次更新作用于test分支上。
三、使用IDEA版本控制
虽然前面我们基本讲解了git的命令行使用方法,但是没有一个图形化界面,始终会感觉到很抽象,所以这里我们使用IDEA来演示,IDEA内部集成了git模块,它可以让我们的git版本管理图形化显示,当然除了IDEA也有一些独立的软件比如:SourceTree(挺好用)
打开IDEA后,找到版本控模块,我们直接点击创建本地仓库,它会自动将当前项目的根目录作为我们的本地仓库,而我们编写的所有代码和项目目录下其他的文件都可以进行版本控制。
我们发现所有项目中正在编写的类文件全部变红了,也就是处于未追踪状态,接着我们进行第一次初始化提交,提交之后我们可以在下方看到所有的本地仓库提交记录。
接着我们来整合一下Web环境,创建新的类之后,IDEA会提示我们是否将文件添加到Git,也就是是否放入暂存区并开启追踪,我们可以直接对比两次代码的相同和不同之处。
接着我们来演示一下分支创建和分支管理。
四、远程仓库
远程仓库实际上就是位于服务器上的仓库,它能在远端保存我们的版本历史,并且可以实现多人同时合作编写项目,每个人都能够同步他人的版本,能够看到他人的版本提交,相当于将我们的代码放在服务器上进行托管。
远程仓库有公有和私有的,公有的远程仓库有GitHub、码云、Coding等,他们都是对外开放的,我们注册账号之后就可以使用远程仓库进行版本控制,其中最大的就是GitHub,但是它服务器在国外,我们国内连接可能会有一点卡。私有的一般是GitLab这种自主搭建的远程仓库私服,在公司中比较常用,它只对公司内部开放,不对外开放。
这里我们以GitHub做讲解,官网:https://github.com,首先完成用户注册。
接下来在 GitHub 中创建一个项目远程仓库(New Repository),依次填写仓库名称、仓库介绍以及私有/公有仓库,创建后便可以得到远程仓库的地址(Https)。
(一)远程账户认证和推送
创建仓库后,我们可以通过推送来将本地仓库中的内容推送到远程仓库。
1 | git remote add 名称 远程仓库地址 |
注意push
后面两个参数,一个是远端名称,还有一个就是本地分支名称,但是如果本地分支名称和远端分支名称一致,那么不用指定远端分支名称,但是如果我们希望推送的分支在远端没有同名的,那么需要额外指定。推送前需要登陆账户,GitHub现在不允许使用用户名密码验证,只允许使用个人AccessToken来验证身份,所以我们需要先去生成一个Token才可以。
注:创建Token时,勾选 repo、gist、user 选项。在终端中push时,账号为GitHub账号名称,密码为Token值。
推送后,我们发现远程仓库中的内容已经与我们本地仓库中的内容保持一致了,注意,远程仓库也可以有很多个分支。
以下部分为SSH配置方式,目前GitHub已支持终端记住Token凭证,因此可忽略该方式。
但是这样比较麻烦,我们每次都需要去输入用户名和密码,有没有一劳永逸的方法呢?当然,我们也可以使用SSH来实现一次性校验,我们可以在本地生成一个rsa公钥:
1 | ssh-keygen -t rsa |
接着我们需要在GitHub上上传我们的公钥,当我们再次去访问GitHub时,会自动验证,就无需进行登录了,之后在Linux部分我们会详细讲解SSH的原理。
接着我们修改一下工作区的内容,提交到本地仓库后,再推送到远程仓库,提交的过程中我们注意观察提交记录:
1 | git commit -a -m 'Modify files' |
我们可以将远端和本地的分支进行绑定,绑定后就不需要指定分支名称了:
1 | git push --set-upstream origin master:master |
在一个本地仓库对应一个远程仓库的情况下,远程仓库基本上就是纯粹的代码托管了(云盘那种感觉,就纯粹是存你代码的)
(二)克隆项目
如果我们已经存在一个远程仓库的情况下,我们需要在远程仓库的代码上继续编写代码,这个时候怎么办呢?
我们可以使用克隆操作来将远端仓库的内容全部复制到本地:
1 | git clone 远程仓库地址 |
这样本地就能够直接与远程保持同步。
(三)抓取、拉取和冲突解决
我们接着来看,如果这个时候,出现多个本地仓库对应一个远程仓库的情况下,比如一个团队里面,N个人都在使用同一个远程仓库,但是他们各自只负责编写和推送自己业务部分的代码,也就是我们常说的协同工作,那么这个时候,我们就需要协调。
比如程序员A完成了他的模块,那么他就可以提交代码并推送到远程仓库,这时程序员B也要开始写代码了,由于远程仓库有其他程序员的提交记录,因此程序员B的本地仓库和远程仓库不一致,这时就需要有先进行pull操作,获取远程仓库中最新的提交:
1 | git fetch 远程仓库 #抓取:只获取但不合并远端分支,后面需要我们手动合并才能提交 |
在程序员B拉取了最新的版本后,再编写自己的代码然后提交就可以实现多人合作编写项目了,并且在拉取过程中就能将别人提交的内容同步到本地,开发效率大大提升。
如果工作中存在不协调的地方,比如现在我们本地有两个仓库,一个仓库去修改hello.txt并直接提交,另一个仓库也修改hello.txt并直接提交,会得到如下错误:
1 | To https://github.com/xx/xxx.git |
一旦一个本地仓库推送了代码,那么另一个本地仓库的推送会被拒绝,原因是当前文件已经被其他的推送给修改了,我们这边相当于是另一个版本,和之前两个分支合并一样,产生了冲突,因此我们只能去解决冲突问题。
如果远程仓库中的提交和本地仓库中的提交没有去编写同一个文件,那么就可以直接拉取:
1 | git pull 远程仓库 |
拉取后会自动进行合并,合并完成之后我们再提交即可。
但是如果两次提交都修改了同一个文件,那么就会遇到和多分支合并一样的情况,在合并时会产生冲突,这时就需要我们自己去解决冲突了。