本文简单的记录一下Git的一些基本的概念和基础的操作,主要是弄懂基本的东西,能够快速的上手并开始使用;但是更多深层的原理和应用还是需要另外去进一步学习的。
创建版本库
我们先整一个git仓库,再来看看基本概念;
新建版本库
- 新建一个项目
默认打开的地址是应该是用户目录,也就是c盘Users下某个地方,下面就先在固定的地址新建一个空的目录作为我们的新项目,叫做FastApiProject
:
$ pwd # 查看刚进入的时候地址
/c/Users/minch
$ cd / # 返回根目录
$ cd /D/Coding/GitHouse/ # 切换到存放项目的地址
$ mkdir FastApiProject # 新建一个文件夹
$ cd ./FastApiProject/ # 切换到新建的目录下
- 创建仓库
在项目目录下,输入git init命令初始化一个Git仓库;现在虽然创建好了,但是依旧是一个空的仓库,创建的时候也有提示;
$ git init
Initialized empty Git repository in D:/Coding/GitHouse/FastApiProject/.git/
版本库基本概念
经过了上面的操作,再看下面的就简单多了;
版本库也就是git仓库,Git 仓库是用于版本控制的一个特殊目录(.git目录
),它保存了项目的完整历史记录和元数据信息。Git 仓库存储了每个提交的快照,以及分支、标签、远程仓库等额外信息。Git 仓库是用于跟踪和管理项目中文件的更改的核心部分。
其实Git仓库就是一个文件夹,一个用来管理代码版本的文件夹。
Git 仓库对应一个存储库,它会记录每次对项目文件的修改。当您在 Git 仓库中进行更改时,Git 会跟踪这些变化并保存它们的历史记录。
这意味着,每当您在项目中添加、修改或删除文件时,Git 都会创建一个新的备份,称为提交(commit
)。提交是代码修改的快照,并包含了作者、时间戳以及相关的元数据信息。
通过这些提交,Git 可以帮助您追踪项目历史,查看特定版本的代码状态,甚至回滚到之前的某个状态。
- Git的元数据保存在.git文件夹里面
.git
文件夹包含了记录代码历史和管理版本控制所需的所有信息。下面是.git
文件夹中常见的一些重要文件和文件夹:
objects
文件夹:存储Git对象,其中包括提交(commit)、树(tree)和Blob对象(即文件内容)。refs
文件夹:存储分支(branch)和标签(tag)引用的文件。例如,refs/heads
存储分支引用,refs/tags
存储标签引用。config
文件:包含了Git 仓库的配置选项,例如用户名、邮箱等。HEAD
文件:指向当前所在分支或提交的引用。index
文件:暂存区(stage)的索引文件,记录即将提交的文件变更信息。logs
文件夹:存储每次操作的日志信息,包括提交日志(commit logs)和引用日志(reflogs)。
.git
文件夹中的这些文件和文件夹(以及其他一些附加文件)共同组成了Git版本库的结构,保存了项目的完整历史记录和相关元数据信息。通过读取和操作.git
文件夹中的内容,Git可以进行版本控制、回溯历史、分支管理等操作。
现在其实就很好理解了,通常.git
文件夹会被放置在项目目录的根目录下。
在项目目录中执行git init
命令来初始化一个新的Git仓库时,Git会在当前目录创建.git
文件夹,并将其作为Git仓库的根目录。这意味着该文件夹将包含Git仓库的所有信息和元数据。
所以正常应用也大可不必想上面那样去创建项目,直接在项目根目录下运行命令即可;
Git工作流程
-
工作区(Working Directory): 工作区是实际进行代码编辑和文件修改的目录,也是开发过程中直接交互的地方。在工作区中,可以创建、编辑、删除文件,并对文件进行各种操作。这些操作仅在本地计算机上进行,不影响其他开发人员或远程仓库中的代码。通常来讲就是某个项目的根目录;
-
本地暂存区(Staging Area): 本地暂存区是位于Git版本库内部的一个临时区域,用于暂存工作区中所做的修改。暂存区主要作用如下:
- 分离工作区和提交: 通过将工作区中的更改添加到暂存区,可以选择性地将一部分更改提交到本地仓库,而不是一次性提交所有更改。这样可以帮助进行更精细的代码管理和版本控制。
- 准备提交的更改: 暂存区可以帮助准备好要提交的更改。可以根据需要在工作区中进行多次修改,然后使用
git add
命令将所需更改添加到暂存区。添加到暂存区后,这些更改就准备好提交到本地仓库中。 - 查看更改内容: 使用
git diff
命令可以比较工作区和暂存区之间的差异,进一步清楚地了解即将提交的更改内容。这可以帮助检查更改是否符合预期,并在提交前进行必要的修改。
-
本地仓库(Local Repository): 本地仓库是Git的核心组成部分,它保存代码仓库的完整历史记录。每次使用
git commit
命令将本地暂存区中的更改提交到本地仓库中时,Git会为该提交创建一个新的版本,并将其永久保存在本地仓库中,也就是上面提到的版本库。主要作用:- 历史记录和版本控制: 本地版本库保存了代码仓库的完整历史记录。每当使用
git commit
命令提交更改时,Git会为该提交创建一个新的版本,并将其永久保存在本地版本库中。通过本地版本库,您可以追溯代码的演变历史,查看每个提交的详细信息,并轻松地进行版本控制。 - 回退和恢复: 本地版本库能够回退到先前的提交状态或恢复到特定的历史版本。通过使用
git checkout
命令,您可以切换到不同的分支、标签或具体的提交。这非常有用,当您需要回退错误的更改、测试旧版本的功能或处理紧急问题时。 - 与远程仓库的同步: 本地版本库可以与远程仓库进行同步,以便与团队共享代码和协作开发。通过使用
git push
命令将本地版本库中的更改推送到远程仓库,并使用git pull
命令从远程仓库拉取最新的更改,可以与其他开发人员保持同步。
- 历史记录和版本控制: 本地版本库保存了代码仓库的完整历史记录。每当使用
-
远程仓库(Remote Repository): 远程仓库是位于云端的Git仓库,可以与团队成员共享代码。通过使用
git push
命令,您可以将本地仓库中的更改推送至远程仓库,以便与他人共享和协作。 -
add
:将工作区中的更改添加到本地暂存区。 -
commit
:将本地暂存区中的更改提交到地仓库,创建一个新的提交。主要完成的内容就是创建一个新的提交,包括暂存区中的所有更改;每个提交都有一个唯一的哈希值,用于在版本历史中标识该提交。提交时,可以提供一条有意义的提交消息来描述更改的内容。
-
checkout
:用于在本地仓库中切换分支或恢复历史版本。主要操作是将Git版本库中的内容拿到工作区。例如回退版本,连续两天提交了版本,第三天的时候,想要将工作区的内容回退到第一天提交的版本,就需要checkout操作回退版本。
或者从一个分支切换到另一个分支,分支的概念看下文;
-
clone
:克隆远程仓库到本地,创建一个本地仓库的副本。克隆操作其实就是一个粘贴复制,把远程的仓库完整的拷贝到本地仓库;通常是包含两步:
- 创建本地仓库:首先,在本地创建一个新的空白目录或指定已存在的目录作为本地仓库。这一步是为了给克隆的项目提供一个位置,用于存储远程仓库的内容和版本历史。
- 克隆仓库:使用
git clone
命令,将远程仓库的内容复制到本地仓库中。克隆操作会自动将远程仓库的全部历史记录、分支信息和文件复制到新创建的本地仓库目录中,并为远程仓库设置一个别名(默认为“origin”)。
-
push
:将本地仓库中的更改推送至远程仓库。将本地的提交推送到远程仓库,更新远程仓库的分支和提交历史。
-
pull
:从远程仓库拉取最新更改(相当于fetch
+merge
)。其实也是两步;更新是从远程仓库(remote repository)到本地仓库(local repository),但实际的合并操作是将更改从本地仓库合并到工作区(working directory)和本地仓库的当前分支。
fetch
:从远程仓库获取最新的提交、分支和标签信息,但不会自动合并到本地分支。merge
:将获取的最新提交合并到当前分支中,以保持与远程仓库同步。
总结一下,git
的流程涉及到四个位置,分别是工作区、暂存区、本地仓库、远程仓库;工作区就是项目目录,就是完整项目的根目录,暂存区和本地仓库都是git在本地工作涉及的两个位置,都位于项目目录下.git
目录下;其次是远程仓库,远程仓库就是类似GitHub、gitee
类的平台,其实就是互联网上的版本库;
完整的流程是新建一个项目,同时新建一个本地库,项目第一版部分代码开发完成后,提交代码到暂存区(add
),等本次开发完成了,就将暂存区打代码提交到本地仓库(commit
);发现有问题或者更新等需要切换版本的时候,就将本地仓库的内容回退到工作区(checkout
);本地仓库完成提交后,就可以将仓库信息给推送到远程仓库存储起来,有修改之后,继续推送到远程仓库(push
);另外的人想要接入项目,就从远程仓库克隆一下仓库,克隆到本地之后(clone
),经过checkout的操作就可以在工作区看到对应版本的代码了;整个流程打通了之后,远程仓库发生修改了,就可以将远程的修改拉回本地(pull
),实际也是拉回本地,进一步将版本切换到工作区;
大的流程如此,但是实际的使用过程还是相对复杂的,还涉及分支、合并等一系列的操作,但是理解这个基本流程后,后面的内容就都好理解了;
分支的基本概念
分支的创建和合并是版本控制系统中比较重要的操作,Git最初就是为了方便世界各地的Linux内核开发者设计的,就是要让分支的创建和合并变的简单而且安全。
分支的概念是比较好理解的,git的版本库就是由很多个分支组成的,我们不创建新的分支的时候,默认就是main/master
分支,也就是主分支,这个名称在安装的时候有提到过;
如果把每次commit
看作一个版本提交,那么上面图片中的每个节点都可以看作一个版本,分支就是在项目的当前状态上创建了一个完全一样的“副本”,这个副本可以独立进行修改,而不影响其他分支或主分支。
在这个新的分支上,可以随意修改代码、添加新的功能、调试和测试,而不会对主分支上的代码产生任何影响。这个分支与主分支相互独立,可以将其看作是一个完整的项目副本。
当在这个分支上进行开发工作时,其他人可以继续在主分支上进行工作,互不干扰。这就是Git分支的优势之一:团队成员可以并行开发不同的功能,而不会影响彼此的工作。
当完成了在分支上的开发工作并测试通过后,可以将这个分支合并回主分支,以将新的功能或修复应用到整个项目中。Git提供了合并分支的功能,它会将分支上所做的更改整合到主分支上。
另外,Git还提供了切换分支的功能,可以在不同的分支之间自由切换。这意味着可以根据需要快速切换到不同的分支,查看或编辑特定的代码。
- 工作目录和分支的关系
分支归根到底是git内的操作,工作目录是怎么样的呢?
当切换到一个新分支时,Git会根据该分支的最后一次提交更新工作目录。这意味着工作目录中的文件和目录会被替换为该分支的最新版本。如果在切换分支之前对工作目录进行了修改,那些修改可能会被保存下来,但在切换到新分支时,它们可能与新分支的代码产生冲突,需要进一步处理。
需要注意的是,未提交的修改不会随着分支的切换而消失。即使切换分支,那些修改仍然存在于工作目录中,只是这些修改可能与当前分支的代码出现冲突。在切换分支之前,可以使用
git stash
命令将这些修改暂存起来,以便稍后在相关分支上继续工作。所以在本地操作的时候,切换分支的时候,工作目录中的内容也会切换;
标签的基本概念
标签就是给定版本的符号名称。它永远都指向相同对象,并且不会变更。标签的用途是,对于所有开发人员来说,都可以使用符号名称引用给定的修订,而且该符号对所有开发人员的意义都是一致的。
在Git中,标签(Tag)是用于给特定提交(commit)打上一个有意义的、永久性的标记。标签相当于一个固定指向某个特定提交的引用,通常用来表示项目的版本、发布或者重要的里程碑。
标签可以用来表示项目的版本号。当代码开发到一个稳定状态并准备发布时,我们可以给这个版本打上一个标签,方便其他人获取并确保他们拿到的是同一个版本的代码。
其次,标签还可以用来管理发布过程。每次发布新版本时,我们可以为这个版本创建一个标签。这样,我们可以方便地回溯、查看和获取这个特定版本的代码,并且同时也能追踪已发布版本的变化和修复。
另外,标签还可以用来标记项目开发过程中的重要里程碑,如测试阶段、功能完成、重要修复等。我们可以给这些重要节点打上标签,以后可以根据标签来查找相关的提交。
Git
有commit
,为什么还要引入tag
?“请把上周一的那个版本打包发布,
commit
号是6a5819e…
”“一串乱七八糟的数字不好找!”
如果换一个办法:
“请把上周一的那个版本打包发布,版本号是
v1.2
”“好的,按照
tag v1.2
查找commit
就行!”所以,
tag
就是一个让人容易记住的有意义的名字,它跟某个commit
绑在一起。
Git基本操作
提交文件到版本库
上面的项目是一个空的项目,里面一个文件都没有,所以先从零开始;
- 在项目中新增一个readme文件
使用touch readme.md
命令新增一个readme的MarkDown文件,这个项目就不是空的了;
- 添加文件到暂存区
将要上传的文件添加到Git的暂存区,使用以下命令:
git add filename // 添加单个文件
git add . // 添加所有文件(包括新的和修改过的)
例如将我们刚才创建的readme.md
上传到暂存区,没有报错就是上传成功了;
我们再新建两个文件,然后上传所有文件,如下,没有报错就成功了;
- 提交代码到Git仓库
将暂存区中的更改提交到代码库,使用以下命令:
git commit -m "Commit message" // 提交并添加提交信息
-m
后面输入的是本次提交的说明,一般用来记录改动记录,这个说明是非常必要的,个人的项目方便回退查看,团队项目方便阅读;
上传结果如下,提交成功后,git会有提示,在这次提交中,共有3个文件被更改,但没有插入或删除任何内容。
常用查看版本库的命令
-
查看提交历史:使用
git log
命令可以查看提交历史,包括每个提交的哈希值、作者、提交日期和提交消息等信息。默认以最新的提交开始显示,按照时间倒序排列。git log
-
查看文件变更:使用
git diff
命令可以比较当前工作目录中的文件与最新提交之间的差异。它可以显示插入的内容、删除的内容以及修改的内容等信息。git diff
-
查看文件状态:使用
git status
命令可以查看工作目录中文件的状态,包括已修改、已暂存、未跟踪等状态。它会列出所有变更的文件以及它们所处的状态。git status
-
查看特定提交的内容:使用
git show
命令可以查看某个特定提交的详细信息,包括提交的更改内容和元数据。需要提供该提交的哈希值或其他引用(如分支名)。git show commit_hash
以上是一些常用的Git命令,用于查看版本库中的内容。通过这些命令,您可以了解提交历史、文件变更以及当前文件的状态,进而进行版本控制和代码调试等操作。
更新版本并提交
上面的操作,相当于是完成了第一版的提交,接着进行文档修改后上传,查看相关的一些变化。
上面初始的版本中,包含三个文件,分别是readme.md、test.py、test2.py
;
向test.py
中写入print("Hello world!")
;
向test2.py
中写入print("Hello test2!")
;
这里写入的方式有很多,建议是使用安装的时候的
vim
就行,也可以通过其他编辑器编辑也行,这无所谓;
- 通过
git diff
查看工作区的文件变更
如下图,运行命令后输出了相关提示;
在输出中的警告表示,在下次Git操作时,LF(Line Feed,换行符)将被CRLF(Carriage Return Line Feed,回车换行符)所取代。这通常发生在Windows环境下,因为Git在Windows上默认使用CRLF作为换行符,而不是Unix风格的LF。这个警告是提醒你关于换行符的差异,但不会影响实际的差异显示和文件修改。
接下来是具体的差异内容,使用---
表示原有文件的位置,+++
表示修改后的文件的位置。在每个文件的差异后面,使用@@ -x,y +z,w @@
格式的行表示差异的位置信息。其中,x,y
表示原有文件中被修改部分的起始行和结束行,z,w
表示修改后的文件中对应的起始行和结束行。
也提示test.py文件添加了一行代码print("Hello world!")
,而test2.py文件也添加了一行代码print("Hello test2!")
。
- 上传test2.py到暂存区
- 使用
git status
查看文件状态
逐行解释:
在main分支中
Changes to be committed
:这一部分列出了即将被提交的修改。在这里,test2.py
文件被修改并已经添加到了暂存区。可以使用
git restore --staged <file>...
命令来取消对文件的暂存操作,将其移出暂存区。
modified
指示被修改还未提交的文件;
Changes not staged for commit
:这一部分列出了未暂存的修改。在这里,test.py
文件被修改但没有被添加到暂存区。可以使用
git add <file>...
命令将文件添加到暂存区,以将其包含在下一次的提交中。
再修改一下readme.md
,并查看多个文件的时候的状态;
修改未暂存:
修改并暂存:
- 提交到Git库中
- 使用
git log
查看版本库内的上传日志
可以看到提交了两次,以及每次提交的时候的基本信息;
- 通过
git show
查看第二次上传的详细信息
就可以看到本次上传的主要信息了;
命令版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。
- 修改test2.py并上传所有文件
这里就可以看到第三次提交的信息,首先是test.py新增了一行代码,其次是test2.py中代码发生了变化;
以上就是git版本更新的基本操作和信息查看;
版本回退
reset/checkout的区别
为什么reset
和checkout
要单独拿出来说,是因为版本回退在git中涉及版本回退有两个常见的操作,当涉及到回退版本或切换分支时,git reset
和git checkout
是两个常用的Git命令,并且有一些区别。
-
git reset
命令:-
作用:
git reset
用于移动HEAD指针和当前分支引用来更改提交历史。 -
提交历史:
git reset
会修改提交历史,因此在公共仓库中使用时需要小心。它可以撤销提交、删除提交或重写提交历史。 -
索引和工作目录:
git reset
根据指定的参数选项(如--mixed
、--soft
和--hard
)来决定是否更改索引和工作目录。--soft
:仅移动HEAD指针和当前分支引用,不更改索引和工作目录。这允许你撤销最近的提交并重新提交。--mixed
(默认选项):移动HEAD指针和当前分支引用,并将索引重置为指定的提交。但是,不更改工作目录。这样可以撤销提交并保留更改的副本供进一步修改。--hard
:彻底移动HEAD指针、当前分支引用和索引,并重置工作目录为指定的提交。这将丢弃所有未提交的更改。
-
-
git checkout
命令:- 作用:
git checkout
用于切换分支、还原文件或查看历史版本。 - 提交历史:
git checkout
不会更改提交历史。它主要用于浏览和查看已经存在的提交。 - 分支和文件:
git checkout
可以通过指定分支或提交标识符,切换到不同的分支或恢复特定版本的文件。它会将HEAD指针和当前分支引用移动到新的目标。
- 作用:
简而言之,git reset
主要用于修改提交历史,并具有对索引和工作目录的不同影响。而git checkout
主要用于切换分支、还原文件和查看历史版本,不会修改提交历史。
git checkout
我们先看看checkout的版本回退操作,至于切换分支等操作,后面再讲,这里只将回退;
为了理解,我们重新创建一个项目,只有一个test.py
文件;
第一版
空
第二版
print("2.0")
第三版
print("3.0")
- 开始回退
现在我们觉得第三次提交的内容中test.py修改的内容不对,我们要回到第二版,在第二版的基础上调整;使用git checkout <commit_id>
就可以实现回退了
如上,就已经完成的切换,Git发出提示:
切换回去之后,就开始没有关联任何分支了,相当于是把那个版本拿出来独立在分支之外了;
也就是说,
checkout
会切换到旧版本,切换回去之后可以查看旧版本的状态,但是他并不能改变提交历史,也就是不管你怎么操作,都不会改变当前分支的提交记录和版本;如果要保留
checkout
之后的修改,可以创建一个新的分支;
- 查看一下这个时候test2.py的内容,可以发现,已经切换成功了
- 接着在第二版的基础上进行修改:
print("2.0——>4.0")
- 进行一下提交,然后查看log
发现已经成功了,但是是在第二版上叠加了一个版本,并不是在第三版的基础上叠加了版本,这就印证了前面说的不会修改分支的提交历史;
- 再切换到主分支查看一下
这个时候有个报错,说切换回main分支的时候,有一个提交不属于任何分支,可以选择创建一个新的分支来保留这个提交。然后可以切换到新的分支上进行开发或修改。
- 看看main分支的log
这里就可以看到,
main
分支的提交历史并没有发生任何变化;那么如何将那个孤立的提交给放到
main
分支里面做第四版呢?其实是不能够直接做到的,那你会问这样的
checkout
有什么意义,当然有,只是流程不能是切换到旧版本,然后修改提交,然后将孤立的那个提交直接拿到旧分支中;两个方案:
- 首先就是按照
git
的提示那样,创建一个新的分支,然后将新分支合并到旧分支中(具体操作在后面的分支去记录);- 其次是我们
checkout
回旧版本后,修改了不要提交,而是将修改暂存,然后切换回旧分支,拉回修改进行合并;
- 演示第二个合并的方案
首先切换到第二版本的分支,然后修改文件,注意这里是重新回到第二版,然后重新修改代码;
上面的修改和提交依旧还存在;
也就是我们最开始切换到第二个版本,修改代码提交的那个‘第四版’;现在不属于任何分支,也称作游离提交;
游离提交无法通过常规的 Git 命令进行删除,提交历史是 Git 存储的一部分,游离提交会在一段时间后被 Git 的垃圾回收机制清理掉。
接着通过git stash save "Your stash message"
保存修改到临时区:
切换回主分支:
查看暂存区内容:
将暂存区的内容应用到当前分支:
这里就开始提示在合并时遇到冲突,由于两个地方都有修改,但是git不知道保留哪个,所以报错,可以看到现在文件里面的内容如下:
需要自行进行编辑选择;这里我们不选择,直接上传,提交;
如上,我们就完成了一个版本的切换;
但是我们发现,这个暂存还在:
可以通过git stash drop <stash_id>
删除:
这样就完成了删除,或者将上面的git stash apply
换成git stash pop
;git会在应用暂存的时候同时删除那个暂存;
git reset
上面,我们的版本库中已经有四个版本了;可以通过git log
可以直接查看(这里博主换了个环境重新搭的,所以hash值和上文不一样,注意区分噢~):
用于回退 Git 提交的通常包含三个命令,它们之间的区别在于对暂存区和工作目录的处理方式不同。
git reset --soft
:
- 这个命令会将当前分支的 HEAD 指针指向指定的提交,同时保留之前的修改内容和暂存区的文件。
- 它不会改变工作目录的文件状态,也不会删除已提交的历史记录。
- 通过这个命令,你可以撤销之前的提交,将其作为未提交的修改保留下来,方便进行新的提交。
git reset --mixed
:
- 这个命令的行为与默认的
git reset
命令相同。- 这个命令会将当前分支的 HEAD 指针指向指定的提交,同时将之前的修改内容放入工作目录,并取消暂存区的文件。
- 它会保留之前的修改作为未暂存的修改,需要重新添加和提交文件。
git reset --hard
:
- 这个命令会彻底丢弃当前分支的 HEAD 指向的提交以及之后的所有提交。
- 它会将当前分支的 HEAD 指针指向指定的提交,并将之前的修改内容从工作目录、暂存区和 Git 历史记录中全部移除。
- 执行这个命令后,之前的修改将无法恢复。
- 注意:在使用这个命令时,请谨慎操作,以免意外丢失重要的修改。
总结:
git reset --soft
:保留修改和暂存区的文件,可重新提交。git reset --mixed
:保留修改但取消暂存,需要重新添加和提交文件。git reset --hard
:彻底丢弃当前提交及之后的修改,无法恢复。
- 准备工作
为了区分工作区、暂存区、git提交历史;首先修改一下最新的文件,然后添加到暂存区,接着再修改一下文件;
在这里,本来是
print("2.0-->4.0")
;暂存区的内容是
print("3.0-->4.0")
;工作区的内容是
print("4.0")
;
- git reset --soft
使用git reset
回退,上一个版本就是HEAD^
,上上一个版本就是HEAD^^
,当然往上100个版本写100个^
比较容易数不过来,所以写成HEAD~100
;
当然也可以使用提交的哈希值来定位某次提交;
运行上面命令,就可以完成回退了,查看提交日志,发现第四次提交已经不再了;
然后分别查看暂存区和工作目录的文件内容;
暂存区如下,可以发现和回退前一致:
工作目录的内容也是和回退前一致的操作;
后悔了怎么办?
后悔了也就太简单了,还是使用git reset --soft
;
但是不能用HEAD了,要找到对应的那个提交的哈希值,在这个案例中就是第四次提交的哈希值:
查看提交的版本中内容、暂存区内容、工作区内容:
版本中和还原前一致
暂存区和还原前一致:
工作区和还原前一致:
- git reset --mixed
同样使用HEAD^
的方式回退,回退成功后给出提示:
表示 test.py
文件有未暂存的更改。由于使用了 --mixed
参数,保留工作区修改但取消暂存。
查看本版本内容,是对应第三版的内容:
暂存区的内容空了:
工作区的内容没变:
这里后悔了的话,是不能完全回去的哈,就是暂存区的内容已经删除了,再回去的话,暂存区的内容也没了;
(如果有办法的话,欢迎纠正哈)
- git reset --hard
在此之前,我们还是将工作区、暂存区的内容调整到最初的一样哈;
然后使用使用HEAD^
的方式回退,命令:git reset --hard HEAD^
;
查看版本中内容,内容回到了第三版的内容:
暂存区内容为空:
工作区内容为空:
这里后悔了,要回到第四版的话,也是很简单的,直接使用命令:
git reset --hard a5b4
即可;同样,回到第四版,也不能恢复暂存区和工作区的内容。
git reset拓展
不仅仅只有上述三种形式的 git reset
命令,还有其他一些变种形式。
除了 git reset --soft
, git reset --mixed
和 git reset --hard
,还有其他的 git reset
变种命令:
-
git reset HEAD <file>
:- 这个命令用于取消已经暂存的文件,将文件从暂存区移回到工作目录。
<file>
参数代表要取消暂存的文件名。
如下,可以将指定的文件从暂存区恢复到工作区。
-
git reset <commit>
:- 这个命令用于将当前分支的 HEAD 指针指向指定的提交。
<commit>
可以是提交的哈希值、分支名或标签名。
这个其实和git reset --mixed是一样的;
-
git reset --keep <commit>
:- 这个命令用于移动当前分支的 HEAD 指针到指定的提交,并保持工作目录的状态不变。
- 它会尝试应用之前提交的更改,如果存在冲突,则命令会终止并保留冲突文件供解决。
- 将当前分支的指针(HEAD)和索引(暂存区)移动到指定的
<commit>
,这样就撤销了<commit>
以及之后的所有提交。 - 不像其他的
reset
模式,--keep
选项会保留工作目录中的所有修改。这意味着未添加到索引的更改不会丢失。 - 如果工作目录存在与
<commit>
不一致的部分,那么这些更改将会被保留,但会被标记为未暂存的更改。
分支的使用
使用分支的好处是可以保持代码库的整洁同时允许并行开发。每个人可以在自己的分支上工作,不会影响到其他人。当一个功能或修复完成后,可以将分支合并回主分支(通常是 master
分支),从而将更改整合到项目中。
默认分支:master
在 Git 中,默认创建的分支通常被称为 master
或 main
分支。这是代码库的主要分支,包含了最新可用的稳定代码。
创建新分支
要创建新的分支,可以使用以下命令:
git branch <branch_name>
这将在当前提交上创建一个名为 <branch_name>
的新分支,但还没有切换到该分支。
如下,没有报错即新建成功!
切换分支
要切换到已存在的分支,可以使用以下命令:
git checkout <branch_name>
这将会将工作目录和代码库切换到名为 <branch_name>
的分支上。
创建并切换分支
git checkout
命令加上-b
参数表示创建并切换:
Git 2.23 版本之后,可以使用以下命令来快速创建并切换到新分支:
git switch -c <branch_name>
这将创建一个名为 <branch_name>
的新分支,并将工作目录和代码库切换到该分支上。
查看分支
要查看所有本地分支,可以运行以下命令:
git branch
当前分支前面会有一个星号 *
。另外,可以添加 -r
选项来查看远程仓库的分支。
提交版本
修改一下文件内容,将里面的内容修改为5.0并提交,都是同样的操作:
合并分支
当在一个分支上工作完成后,通常需要将其合并回主分支或其他目标分支。要合并分支,可以使用以下命令:
git merge <branch_name>
这将把名为 <branch_name>
的分支合并到当前所在的分支(通常是 master
分支)。
如下,先切换回主分支,然后将分支branch1
合并到当前分支,然后查看提交历史:
删除分支
当分支的任务完成后,可以删除不再需要的分支。要删除分支,可以使用以下命令:
git branch -d <branch_name>
这将删除名为 <branch_name>
的分支。如果分支上还有未合并的更改,需要使用 -D
参数来强制删除。
switch命令
Git 2.23 版本引入了 git switch
命令,用于切换分支和创建新分支。git switch
命令的功能与之前的 git checkout
命令有所重叠,但是更加直观和易于使用。
1. 切换到已存在的分支
要切换到已存在的分支,可以使用以下命令:
git switch <branch_name>
这将使当前工作目录切换到名为 <branch_name>
的分支。
2. 创建并切换到新分支
要创建一个新分支并立即切换到该分支,可以使用以下命令:
git switch -c <new_branch_name>
这将创建一个名为 <new_branch_name>
的新分支,并将当前工作目录切换到该分支。
3. 切换到远程分支
对于一个远程分支,你可能需要先将其拉取到本地,然后再切换到该分支。可以使用以下命令完成这个过程:
git switch --track <remote_branch_name>
这将拉取名为 <remote_branch_name>
的远程分支并创建一个与其关联的本地分支,并将当前工作目录切换到该分支。
4. 强制切换分支
如果在切换分支时存在未提交的更改,Git 默认情况下会阻止你切换分支。然而,有时你可能希望强制切换分支并放弃未提交的更改。可以使用以下命令:
git switch -f <branch_name>
这将强制将当前工作目录切换到名为 <branch_name>
的分支,并丢弃未提交的更改。
switch 和 checkout对比
git switch
和git checkout
是在 Git 中用于切换分支的两个命令,它们有一些区别。以下是它们之间的主要区别:
- 操作方式不同:
git switch
的操作方式更加直观和一致。它专门用于切换分支和创建新分支,更符合用户的直觉。而git checkout
则具有更多的功能,可以用于切换分支、创建新分支、恢复文件等。- 引起修改的情况不同: 在某些情况下,使用
git checkout
可能会导致未提交的更改被覆盖或丢失。例如,在切换分支之前,如果有对当前分支已修改但尚未提交的文件进行更改,那么git checkout
会直接将这些更改应用到目标分支。这可能会导致不可预料的结果。相比之下,git switch
不会自动应用未提交的更改,它会提醒你先处理这些更改,然后再切换分支。- 语义化的分支操作:
git switch
的命令参数和选项更加语义化和直观。例如,使用-c
选项来创建并切换到一个新分支,使用--detach
选项来切换到一个游离的 HEAD(不指向任何分支)。这使得分支操作更加易于理解和记忆。- 后续开发和推荐性:
git switch
是在 Git 2.23 版本中引入的新命令,而git checkout
是 Git 的旧命令。随着时间的推移,Git 社区更倾向于使用和推荐git switch
命令,因为它更直观、功能单一,并且在处理未提交的更改时更加安全。需要注意的是,虽然
git switch
更加推荐使用,但在早期版本的 Git 中仍然可以使用git checkout
命令,并且它仍然是有效的。对于一些高级或特殊的用例,git checkout
可能提供更多灵活性和功能。总的来说,
git switch
是一个更简洁、直观并且推荐使用的命令,专门用于分支切换和创建新分支。而git checkout
则是一个更通用、功能更多的命令,可以用于更多其他场景,如恢复文件、创建或删除分支等。
标签的使用
添加标签
切换到对应的分支,使用命令:git tag tagname
为最新的提交打标签:
使用命令git tag
查看所有标签:
为历史提交打标签
之前某次提交忘记打标签了,为其打标签:
首先查看历史提交,git log --pretty=oneline --abbrev-commit
,该命令的作用是以单行的形式显示提交历史,并使用缩略的提交哈希值。
为第一版打标签:
用git show <tagname>
查看标签信息:
删除标签
使用git tag -d tagname
即可完成标签的删除;
远程仓库
远程仓库有很多,流行的主要就有github、gitee等,这里使用gitee来演示,创建账号和新建仓库具体操作就不演示了;
添加到远程仓库
- 首先新建一个远程仓库
这里新建一个test_gitee的远程仓库;
-
使用
git remote add
命令,并将<remote_name>
替换为想要的远程仓库名称,<remote_url>
替换为远程仓库的 URL。使用git remote -v
查看远程仓库详细信息:这里是可以添加多个远程仓库的噢
-
推送到远程仓库
使用
git push
命令将本地仓库中的分支推送到远程仓库。指定要推送的分支,以及远程仓库的名称和分支。例如,以下命令将本地main
分支推送到名为origin
的远程仓库。Copy Codegit push <remote_name> <branch_name>
如果是首次推送分支,可能需要使用
-u
参数来设置跟踪。例如:Copy Codegit push -u <remote_name> <branch_name>
如下,便完成了推送:
Enumerating objects: 12, done.
:Git 正在遍历要推送的对象。Counting objects: 100% (12/12), done.
:Git 统计了要推送的对象数量。Delta compression using up to 16 threads
:Git 使用了多线程进行增量压缩。Compressing objects: 100% (4/4), done.
:Git 完成了对象的压缩。Writing objects: 100% (12/12), 947 bytes | 947.00 KiB/s, done.
:Git 将对象写入远程仓库。Total 12 (delta 0), reused 0 (delta 0), pack-reused 0
:Git 显示了推送的总体情况,此次推送没有增量数据(delta)。remote: Powered by GITEE.COM [GNK-6.4]
:远程仓库的信息,显示你正在使用 Gitee 平台。To https://gitee.com/zishu-yanluo/test_gitee.git
:推送的目标远程仓库 URL。[new branch] master -> master
:远程仓库中创建了一个新分支master
,与本地的master
分支关联。branch 'master' set up to track 'test_gitee/master'.
:本地的master
分支已配置跟踪远程仓库的test_gitee/master
分支。
在远程仓库中也可以查看到我们的提交了:
拉取远程仓库
从远程仓库中获取最新的代码更新是很重要的,就像从云盘上下载最新的文件到你的电脑一样。要拉取远程仓库的更新,需要执git pull
操作:
git pull
命令的一般语法为:
git pull <远程仓库名> <远程分支名>
具体解释如下:
<远程仓库名>
:指定要获取更新的远程仓库,通常是使用origin
来表示默认远程仓库。<远程分支名>
:指定要获取更新的远程分支。
git pull
命令的执行过程大致如下:
- 首先,它会自动调用
git fetch
命令,从指定的远程仓库中获取最新的提交,但不会应用到本地分支。 - 然后,它会自动调用
git merge
命令,将获取的提交与当前分支进行合并。
在执行 git pull
命令时,可能会遇到以下情况:
- 如果本地没有未提交的修改,
git pull
会自动合并远程分支的更新到当前分支,并创建一个新的合并提交。 - 如果本地有未提交的修改,
git pull
默认会尝试自动合并。如果合并过程中发生冲突,你需要手动解决冲突后再提交。 - 如果你想要强制执行
git pull
,可以使用git pull --force
命令。
另外,还有一些 git pull
命令的选项可以进一步控制其行为,例如:
--rebase
:使用 rebase 而不是 merge 来合并远程分支的更新。--no-commit
:获取远程更新后不自动创建新的合并提交。--ff-only
:仅在快进合并的情况下才执行合并操作,否则终止。
如下,现在远程仓库的版本是第四次提交:
现在新建一个分支并回退到第三版:
运行git pull
命令没报错即拉取成功:
克隆远程仓库
在使用 git clone
命令进行克隆时,你有两种选择:
-
克隆到新建的项目目录:你可以指定一个新的项目目录,在该目录下执行
git clone
命令来克隆远程仓库。这将在指定的目录中创建一个新的项目,并将远程仓库的内容复制到该目录中。例如:
git clone <远程仓库地址> <新项目目录>
在这种情况下,
git clone
命令会自动创建一个与远程仓库同名的项目目录,并将远程仓库的内容复制到该目录中。 -
克隆到已存在的项目目录:如果你想将远程仓库的内容复制到一个已存在的项目目录中,可以直接进入该目录,并执行
git clone
命令。这将在当前目录中创建一个新的分支,并将远程仓库的内容复制到该分支中。例如:
cd <已存在的项目目录> git clone <远程仓库地址>
在这种情况下,
git clone
命令会将远程仓库的内容复制到当前目录中,并自动创建一个新的默认分支。
- 这里以很火的java开源博客系统halo为例:
注意:
默认情况下,
git clone
命令会克隆远程仓库的所有分支。但是,克隆下来的分支在本地仓库中会以远程分支的形式存在,并不会自动创建与每个远程分支对应的本地分支。你可以使用
git branch -r
命令查看克隆下来的所有远程分支,使用git branch -a
命令查看所有本地分支和远程分支。要将远程分支创建为本地分支,可以使用以下命令:
git checkout -b <本地分支名> <远程仓库名/远程分支名>
这将创建一个新的本地分支,并将其设置为指定远程分支的跟踪分支。
另外,如果你只想克隆特定的分支而不是所有分支,可以使用
--single-branch
选项。例如:git clone --single-branch -b <分支名> <远程仓库地址>
这样只会克隆指定的分支,并忽略其他分支。
远程分支与标签操作
-
分支
-
创建远程分支并推送:要在本地创建一个新分支,并将其推送到远程仓库,可以使用以下命令:
git checkout -b <branch-name> git push origin <branch-name>
这将创建一个名为
<branch-name>
的新分支,并将其推送到名为origin
的远程仓库。 -
删除远程分支:要删除远程仓库中的分支,可以使用以下命令:
git push origin --delete <branch-name>
这将从远程仓库中删除名为
<branch-name>
的分支。请确保你有足够的权限来执行该操作。 -
查看远程分支:要查看远程仓库中的分支,可以使用以下命令:
git branch -r
这将显示远程仓库中的所有分支。
-
拉取远程分支:要将远程仓库的特定分支拉取到本地仓库,可以使用以下命令:
git checkout -t origin/<branch-name>
这将创建一个与远程仓库中的
<branch-name>
分支相对应的本地分支,并将其切换到该分支。
-
-
标签
创建的标签都只存储在本地,不会自动推送到远程。
需要使用git push origin <tag-name>
命令显式地将标签推送到远程仓库。
或者推送全部标签:git push origin --tags
可以使用git tag -d <tag-name>
命令删除本地的标签。类似地,通过git push origin :refs/tags/<tag-name>
命令可以从远程仓库删除标签。
具体用法如下:
<tag-name>
表示你要删除的标签的名称。origin
是远程仓库的名称,通常指向你的远程仓库URL。- 在命令中使用冒号(:)来指示删除操作,冒号前为空,表示引用的空值。
举个例子,如果你想删除名为
v1.0
的标签,可以执行以下命令:Copy Codegit push origin :refs/tags/v1.0
执行命令后,Git会将该命令推送到远程仓库,远程仓库会删除对应的标签。
需要注意的是,这个命令只会删除远程仓库中的标签,而不会影响本地仓库中的标签。
Q&A
两本地仓库有一个同样的分支,同时推送到远程仓库会怎么样?
如果两个人的本地仓库都有一个同样的分支,并且同时推送到远程仓库,会导致冲突的发生。这是因为远程仓库不能直接处理两个相互冲突的提交。
具体情况如下:
- 假设两个人(Person A和Person B)都从远程仓库克隆了一个相同的分支,并在各自的本地仓库中进行了修改。
- Person A 先完成了修改并将其推送到远程仓库。
- 接下来,Person B 也希望将自己的修改推送到远程仓库。然而,由于此时远程仓库已经包含了 Person A 的提交,Person B 的推送会被拒绝,并且提示存在冲突。
在这种情况下,解决冲突的方法如下:
- Person B 需要先拉取最新的远程更新到本地仓库,使用
git pull
命令。git pull
命令会合并远程分支的更改到本地分支,并且可能触发冲突。- 如果发生冲突,Person B 需要手动解决冲突。打开包含冲突的文件,根据标记手动编辑文件,解决冲突并保留需要的更改。
- 解决冲突后,使用
git add
命令将修改的文件标记为已解决冲突。- 最后,使用
git commit
命令提交解决冲突后的更改。此时,会生成一个新的合并提交。- 接下来,Person B 可以再次尝试推送自己的提交到远程仓库。
总之,如果两个人的本地仓库都有相同的分支,并且同时推送到远程仓库,会导致冲突的发生。在这种情况下,需要先拉取最新的远程更新,解决冲突后再推送修改到远程仓库。这样可以确保所有人的更改都能够合并,并保持代码的一致性。
评论区