前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Git使用教程(看完会了也懂了)

Git使用教程(看完会了也懂了)

作者头像
MinChess
发布2023-08-13 10:36:40
3390
发布2023-08-13 10:36:40
举报
文章被收录于专栏:九陌斋九陌斋

本文简单的记录一下Git的一些基本的概念和基础的操作,主要是弄懂基本的东西,能够快速的上手并开始使用;但是更多深层的原理和应用还是需要另外去进一步学习的。

创建版本库

我们先整一个git仓库,再来看看基本概念;

新建版本库

  • 新建一个项目

默认打开的地址是应该是用户目录,也就是c盘Users下某个地方,下面就先在固定的地址新建一个空的目录作为我们的新项目,叫做FastApiProject

代码语言:javascript
复制
$ pwd # 查看刚进入的时候地址
/c/Users/minch

$ cd / # 返回根目录

$ cd /D/Coding/GitHouse/ # 切换到存放项目的地址

$ mkdir FastApiProject # 新建一个文件夹

$ cd ./FastApiProject/ # 切换到新建的目录下
  • 创建仓库

在项目目录下,输入git init命令初始化一个Git仓库;现在虽然创建好了,但是依旧是一个空的仓库,创建的时候也有提示;

代码语言:javascript
复制
$ git init
Initialized empty Git repository in D:/Coding/GitHouse/FastApiProject/.git/
image-20230629173600443
image-20230629173600443

版本库基本概念

经过了上面的操作,再看下面的就简单多了;

版本库也就是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版本库内部的一个临时区域,用于暂存工作区中所做的修改。暂存区主要作用如下:
    1. 分离工作区和提交: 通过将工作区中的更改添加到暂存区,可以选择性地将一部分更改提交到本地仓库,而不是一次性提交所有更改。这样可以帮助进行更精细的代码管理和版本控制。
    2. 准备提交的更改: 暂存区可以帮助准备好要提交的更改。可以根据需要在工作区中进行多次修改,然后使用git add命令将所需更改添加到暂存区。添加到暂存区后,这些更改就准备好提交到本地仓库中。
    3. 查看更改内容: 使用git diff命令可以比较工作区和暂存区之间的差异,进一步清楚地了解即将提交的更改内容。这可以帮助检查更改是否符合预期,并在提交前进行必要的修改。
  • 本地仓库(Local Repository): 本地仓库是Git的核心组成部分,它保存代码仓库的完整历史记录。每次使用git commit命令将本地暂存区中的更改提交到本地仓库中时,Git会为该提交创建一个新的版本,并将其永久保存在本地仓库中,也就是上面提到的版本库。主要作用:
    1. 历史记录和版本控制: 本地版本库保存了代码仓库的完整历史记录。每当使用git commit命令提交更改时,Git会为该提交创建一个新的版本,并将其永久保存在本地版本库中。通过本地版本库,您可以追溯代码的演变历史,查看每个提交的详细信息,并轻松地进行版本控制。
    2. 回退和恢复: 本地版本库能够回退到先前的提交状态或恢复到特定的历史版本。通过使用git checkout命令,您可以切换到不同的分支、标签或具体的提交。这非常有用,当您需要回退错误的更改、测试旧版本的功能或处理紧急问题时。
    3. 与远程仓库的同步: 本地版本库可以与远程仓库进行同步,以便与团队共享代码和协作开发。通过使用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:将获取的最新提交合并到当前分支中,以保持与远程仓库同步。
image-20230629214712288
image-20230629214712288

总结一下,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)打上一个有意义的、永久性的标记。标签相当于一个固定指向某个特定提交的引用,通常用来表示项目的版本、发布或者重要的里程碑。

标签可以用来表示项目的版本号。当代码开发到一个稳定状态并准备发布时,我们可以给这个版本打上一个标签,方便其他人获取并确保他们拿到的是同一个版本的代码。

其次,标签还可以用来管理发布过程。每次发布新版本时,我们可以为这个版本创建一个标签。这样,我们可以方便地回溯、查看和获取这个特定版本的代码,并且同时也能追踪已发布版本的变化和修复。

另外,标签还可以用来标记项目开发过程中的重要里程碑,如测试阶段、功能完成、重要修复等。我们可以给这些重要节点打上标签,以后可以根据标签来查找相关的提交。

Gitcommit,为什么还要引入tag? “请把上周一的那个版本打包发布,commit号是6a5819e…” “一串乱七八糟的数字不好找!” 如果换一个办法: “请把上周一的那个版本打包发布,版本号是v1.2” “好的,按照tag v1.2查找commit就行!” 所以,tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。

Git基本操作

提交文件到版本库

上面的项目是一个空的项目,里面一个文件都没有,所以先从零开始;

  • 在项目中新增一个readme文件

使用touch readme.md命令新增一个readme的MarkDown文件,这个项目就不是空的了;

  • 添加文件到暂存区

将要上传的文件添加到Git的暂存区,使用以下命令:

代码语言:javascript
复制
git add filename  // 添加单个文件
git add .         // 添加所有文件(包括新的和修改过的)

例如将我们刚才创建的readme.md上传到暂存区,没有报错就是上传成功了;

image-20230629215928747
image-20230629215928747

我们再新建两个文件,然后上传所有文件,如下,没有报错就成功了;

image-20230629220133850
image-20230629220133850
  • 提交代码到Git仓库

将暂存区中的更改提交到代码库,使用以下命令:

代码语言:javascript
复制
git commit -m "Commit message"  // 提交并添加提交信息

-m后面输入的是本次提交的说明,一般用来记录改动记录,这个说明是非常必要的,个人的项目方便回退查看,团队项目方便阅读;

上传结果如下,提交成功后,git会有提示,在这次提交中,共有3个文件被更改,但没有插入或删除任何内容。

image-20230629220333325
image-20230629220333325

常用查看版本库的命令

查看提交历史:使用git log命令可以查看提交历史,包括每个提交的哈希值、作者、提交日期和提交消息等信息。默认以最新的提交开始显示,按照时间倒序排列。

代码语言:javascript
复制
git log

查看文件变更:使用git diff命令可以比较当前工作目录中的文件与最新提交之间的差异。它可以显示插入的内容、删除的内容以及修改的内容等信息。

代码语言:javascript
复制
git diff

查看文件状态:使用git status命令可以查看工作目录中文件的状态,包括已修改、已暂存、未跟踪等状态。它会列出所有变更的文件以及它们所处的状态。

代码语言:javascript
复制
git status

查看特定提交的内容:使用git show命令可以查看某个特定提交的详细信息,包括提交的更改内容和元数据。需要提供该提交的哈希值或其他引用(如分支名)。

代码语言:javascript
复制
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!")

image-20230630202201485
image-20230630202201485
  • 上传test2.py到暂存区
image-20230630203232466
image-20230630203232466
  • 使用git status查看文件状态

逐行解释:

在main分支中 Changes to be committed:这一部分列出了即将被提交的修改。在这里,test2.py文件被修改并已经添加到了暂存区。 可以使用git restore --staged <file>...命令来取消对文件的暂存操作,将其移出暂存区。 modified指示被修改还未提交的文件; Changes not staged for commit:这一部分列出了未暂存的修改。在这里,test.py文件被修改但没有被添加到暂存区。 可以使用git add <file>...命令将文件添加到暂存区,以将其包含在下一次的提交中。

image-20230630203322295
image-20230630203322295

再修改一下readme.md,并查看多个文件的时候的状态;

修改未暂存:

image-20230630204058223
image-20230630204058223

修改并暂存:

image-20230630204133843
image-20230630204133843
  • 提交到Git库中
image-20230630210742108
image-20230630210742108
  • 使用git log查看版本库内的上传日志

可以看到提交了两次,以及每次提交的时候的基本信息;

image-20230630210931876
image-20230630210931876
  • 通过git show查看第二次上传的详细信息

就可以看到本次上传的主要信息了;

命令版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。

image-20230630211230983
image-20230630211230983
  • 修改test2.py并上传所有文件

这里就可以看到第三次提交的信息,首先是test.py新增了一行代码,其次是test2.py中代码发生了变化;

image-20230630212142222
image-20230630212142222

以上就是git版本更新的基本操作和信息查看;

版本回退

reset/checkout的区别

为什么resetcheckout要单独拿出来说,是因为版本回退在git中涉及版本回退有两个常见的操作,当涉及到回退版本或切换分支时,git resetgit checkout是两个常用的Git命令,并且有一些区别。

  1. git reset命令:
    • 作用:git reset用于移动HEAD指针和当前分支引用来更改提交历史。
    • 提交历史:git reset会修改提交历史,因此在公共仓库中使用时需要小心。它可以撤销提交、删除提交或重写提交历史。
    • 索引和工作目录:git reset根据指定的参数选项(如--mixed--soft--hard)来决定是否更改索引和工作目录。
      • --soft:仅移动HEAD指针和当前分支引用,不更改索引和工作目录。这允许你撤销最近的提交并重新提交。
      • --mixed(默认选项):移动HEAD指针和当前分支引用,并将索引重置为指定的提交。但是,不更改工作目录。这样可以撤销提交并保留更改的副本供进一步修改。
      • --hard:彻底移动HEAD指针、当前分支引用和索引,并重置工作目录为指定的提交。这将丢弃所有未提交的更改。
  2. git checkout命令:
    • 作用:git checkout用于切换分支、还原文件或查看历史版本。
    • 提交历史:git checkout不会更改提交历史。它主要用于浏览和查看已经存在的提交。
    • 分支和文件:git checkout可以通过指定分支或提交标识符,切换到不同的分支或恢复特定版本的文件。它会将HEAD指针和当前分支引用移动到新的目标。

简而言之,git reset主要用于修改提交历史,并具有对索引和工作目录的不同影响。而git checkout主要用于切换分支、还原文件和查看历史版本,不会修改提交历史。

git checkout

我们先看看checkout的版本回退操作,至于切换分支等操作,后面再讲,这里只将回退;

为了理解,我们重新创建一个项目,只有一个test.py文件;

第一版

代码语言:javascript
复制

第二版

代码语言:javascript
复制
print("2.0")

第三版

代码语言:javascript
复制
print("3.0")
image-20230702132319235
image-20230702132319235
  • 开始回退

现在我们觉得第三次提交的内容中test.py修改的内容不对,我们要回到第二版,在第二版的基础上调整;使用git checkout <commit_id>就可以实现回退了

image-20230702132516119
image-20230702132516119

如上,就已经完成的切换,Git发出提示: 切换回去之后,就开始没有关联任何分支了,相当于是把那个版本拿出来独立在分支之外了; 也就是说,checkout会切换到旧版本,切换回去之后可以查看旧版本的状态,但是他并不能改变提交历史,也就是不管你怎么操作,都不会改变当前分支的提交记录和版本; 如果要保留checkout之后的修改,可以创建一个新的分支;

  • 查看一下这个时候test2.py的内容,可以发现,已经切换成功了
image-20230702124708614
image-20230702124708614
  • 接着在第二版的基础上进行修改:
代码语言:javascript
复制
print("2.0——>4.0")
  • 进行一下提交,然后查看log
image-20230702154850384
image-20230702154850384

发现已经成功了,但是是在第二版上叠加了一个版本,并不是在第三版的基础上叠加了版本,这就印证了前面说的不会修改分支的提交历史;

  • 再切换到主分支查看一下
image-20230702154706084
image-20230702154706084

这个时候有个报错,说切换回main分支的时候,有一个提交不属于任何分支,可以选择创建一个新的分支来保留这个提交。然后可以切换到新的分支上进行开发或修改。

  • 看看main分支的log
image-20230702155409735
image-20230702155409735

这里就可以看到,main分支的提交历史并没有发生任何变化; 那么如何将那个孤立的提交给放到main分支里面做第四版呢? 其实是不能够直接做到的,那你会问这样的checkout有什么意义,当然有,只是流程不能是切换到旧版本,然后修改提交,然后将孤立的那个提交直接拿到旧分支中;两个方案:

  • 首先就是按照git的提示那样,创建一个新的分支,然后将新分支合并到旧分支中(具体操作在后面的分支去记录);
  • 其次是我们checkout回旧版本后,修改了不要提交,而是将修改暂存,然后切换回旧分支,拉回修改进行合并;
  • 演示第二个合并的方案

首先切换到第二版本的分支,然后修改文件,注意这里是重新回到第二版,然后重新修改代码

上面的修改和提交依旧还存在;

也就是我们最开始切换到第二个版本,修改代码提交的那个‘第四版’;现在不属于任何分支,也称作游离提交; 游离提交无法通过常规的 Git 命令进行删除,提交历史是 Git 存储的一部分,游离提交会在一段时间后被 Git 的垃圾回收机制清理掉。

接着通过git stash save "Your stash message"保存修改到临时区:

image-20230702164120331
image-20230702164120331

切换回主分支:

image-20230702164136155
image-20230702164136155

查看暂存区内容:

image-20230702164830620
image-20230702164830620

将暂存区的内容应用到当前分支:

image-20230702164850595
image-20230702164850595

这里就开始提示在合并时遇到冲突,由于两个地方都有修改,但是git不知道保留哪个,所以报错,可以看到现在文件里面的内容如下:

image-20230702165837341
image-20230702165837341

需要自行进行编辑选择;这里我们不选择,直接上传,提交;

image-20230702165134414
image-20230702165134414

如上,我们就完成了一个版本的切换;

但是我们发现,这个暂存还在:

image-20230702165340659
image-20230702165340659

可以通过git stash drop <stash_id>删除:

image-20230702165623429
image-20230702165623429

这样就完成了删除,或者将上面的git stash apply换成git stash pop;git会在应用暂存的时候同时删除那个暂存;

git reset

上面,我们的版本库中已经有四个版本了;可以通过git log可以直接查看(这里博主换了个环境重新搭的,所以hash值和上文不一样,注意区分噢~):

image-20230728171307393
image-20230728171307393
工艺流程图
工艺流程图

用于回退 Git 提交的通常包含三个命令,它们之间的区别在于对暂存区和工作目录的处理方式不同。

  1. git reset --soft:
    • 这个命令会将当前分支的 HEAD 指针指向指定的提交,同时保留之前的修改内容和暂存区的文件
    • 它不会改变工作目录的文件状态,也不会删除已提交的历史记录。
    • 通过这个命令,你可以撤销之前的提交,将其作为未提交的修改保留下来,方便进行新的提交。
  2. git reset --mixed:
    • 这个命令的行为与默认的 git reset 命令相同。
    • 这个命令会将当前分支的 HEAD 指针指向指定的提交,同时将之前的修改内容放入工作目录,并取消暂存区的文件
    • 它会保留之前的修改作为未暂存的修改,需要重新添加和提交文件。
  3. 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")

image-20230728171423388
image-20230728171423388

工作区的内容是print("4.0")

image-20230728171455609
image-20230728171455609
  • git reset --soft

使用git reset回退,上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100;

当然也可以使用提交的哈希值来定位某次提交;

image-20230728171655930
image-20230728171655930

运行上面命令,就可以完成回退了,查看提交日志,发现第四次提交已经不再了;

image-20230728171705690
image-20230728171705690

然后分别查看暂存区和工作目录的文件内容;

暂存区如下,可以发现和回退前一致:

image-20230728171849828
image-20230728171849828

工作目录的内容也是和回退前一致的操作;

image-20230728171923797
image-20230728171923797

后悔了怎么办

后悔了也就太简单了,还是使用git reset --soft

但是不能用HEAD了,要找到对应的那个提交的哈希值,在这个案例中就是第四次提交的哈希值:

image-20230728174311349
image-20230728174311349

查看提交的版本中内容、暂存区内容、工作区内容:

版本中和还原前一致

image-20230728174348110
image-20230728174348110

暂存区和还原前一致:

image-20230728174415990
image-20230728174415990

工作区和还原前一致:

image-20230728174446776
image-20230728174446776
  • git reset --mixed

同样使用HEAD^的方式回退,回退成功后给出提示:

表示 test.py 文件有未暂存的更改。由于使用了 --mixed 参数,保留工作区修改但取消暂存。

image-20230728175116504
image-20230728175116504

查看本版本内容,是对应第三版的内容:

image-20230728175336523
image-20230728175336523

暂存区的内容空了:

image-20230728175306190
image-20230728175306190

工作区的内容没变:

image-20230728174740140
image-20230728174740140

这里后悔了的话,是不能完全回去的哈,就是暂存区的内容已经删除了,再回去的话,暂存区的内容也没了; (如果有办法的话,欢迎纠正哈)

  • git reset --hard

在此之前,我们还是将工作区、暂存区的内容调整到最初的一样哈;

image-20230730171140275
image-20230730171140275

然后使用使用HEAD^的方式回退,命令:git reset --hard HEAD^

查看版本中内容,内容回到了第三版的内容:

image-20230730171449666
image-20230730171449666

暂存区内容为空:

image-20230730171457435
image-20230730171457435

工作区内容为空:

image-20230730171521834
image-20230730171521834

这里后悔了,要回到第四版的话,也是很简单的,直接使用命令:git reset --hard a5b4即可; 同样,回到第四版,也不能恢复暂存区和工作区的内容。

git reset拓展

不仅仅只有上述三种形式的 git reset 命令,还有其他一些变种形式。

除了 git reset --soft, git reset --mixedgit reset --hard,还有其他的 git reset 变种命令:

  1. git reset HEAD <file>:
    • 这个命令用于取消已经暂存的文件,将文件从暂存区移回到工作目录。
    • <file> 参数代表要取消暂存的文件名。

    如下,可以将指定的文件从暂存区恢复到工作区。

image-20230730180725398
image-20230730180725398
  1. git reset <commit>:
    • 这个命令用于将当前分支的 HEAD 指针指向指定的提交。
    • <commit> 可以是提交的哈希值、分支名或标签名。

    这个其实和git reset --mixed是一样的;

  2. git reset --keep <commit>:
    • 这个命令用于移动当前分支的 HEAD 指针到指定的提交,并保持工作目录的状态不变。
    • 它会尝试应用之前提交的更改,如果存在冲突,则命令会终止并保留冲突文件供解决。
    1. 将当前分支的指针(HEAD)和索引(暂存区)移动到指定的 <commit>,这样就撤销了 <commit> 以及之后的所有提交。
    2. 不像其他的 reset 模式,--keep 选项会保留工作目录中的所有修改。这意味着未添加到索引的更改不会丢失。
    3. 如果工作目录存在与 <commit> 不一致的部分,那么这些更改将会被保留,但会被标记为未暂存的更改。

分支的使用

使用分支的好处是可以保持代码库的整洁同时允许并行开发。每个人可以在自己的分支上工作,不会影响到其他人。当一个功能或修复完成后,可以将分支合并回主分支(通常是 master 分支),从而将更改整合到项目中。

默认分支:master

在 Git 中,默认创建的分支通常被称为 mastermain 分支。这是代码库的主要分支,包含了最新可用的稳定代码。

创建新分支

要创建新的分支,可以使用以下命令:

代码语言:javascript
复制
git branch <branch_name>

这将在当前提交上创建一个名为 <branch_name> 的新分支,但还没有切换到该分支。

如下,没有报错即新建成功!

image-20230801172158836
image-20230801172158836

切换分支

要切换到已存在的分支,可以使用以下命令:

代码语言:javascript
复制
git checkout <branch_name>

这将会将工作目录和代码库切换到名为 <branch_name> 的分支上。

image-20230801172135789
image-20230801172135789

创建并切换分支

git checkout命令加上-b参数表示创建并切换:

image-20230801173254086
image-20230801173254086

Git 2.23 版本之后,可以使用以下命令来快速创建并切换到新分支:

代码语言:javascript
复制
git switch -c <branch_name>

这将创建一个名为 <branch_name> 的新分支,并将工作目录和代码库切换到该分支上。

image-20230801172556689
image-20230801172556689

查看分支

要查看所有本地分支,可以运行以下命令:

代码语言:javascript
复制
git branch

当前分支前面会有一个星号 *。另外,可以添加 -r 选项来查看远程仓库的分支。

image-20230801172912690
image-20230801172912690

提交版本

修改一下文件内容,将里面的内容修改为5.0并提交,都是同样的操作:

image-20230801173506032
image-20230801173506032

合并分支

当在一个分支上工作完成后,通常需要将其合并回主分支或其他目标分支。要合并分支,可以使用以下命令:

代码语言:javascript
复制
git merge <branch_name>

这将把名为 <branch_name> 的分支合并到当前所在的分支(通常是 master 分支)。

如下,先切换回主分支,然后将分支branch1合并到当前分支,然后查看提交历史:

image-20230801173959271
image-20230801173959271

删除分支

当分支的任务完成后,可以删除不再需要的分支。要删除分支,可以使用以下命令:

代码语言:javascript
复制
git branch -d <branch_name>

这将删除名为 <branch_name> 的分支。如果分支上还有未合并的更改,需要使用 -D 参数来强制删除。

switch命令

Git 2.23 版本引入了 git switch 命令,用于切换分支和创建新分支。git switch 命令的功能与之前的 git checkout 命令有所重叠,但是更加直观和易于使用。

1. 切换到已存在的分支

要切换到已存在的分支,可以使用以下命令:

代码语言:javascript
复制
git switch <branch_name>

这将使当前工作目录切换到名为 <branch_name> 的分支。

2. 创建并切换到新分支

要创建一个新分支并立即切换到该分支,可以使用以下命令:

代码语言:javascript
复制
git switch -c <new_branch_name>

这将创建一个名为 <new_branch_name> 的新分支,并将当前工作目录切换到该分支。

3. 切换到远程分支

对于一个远程分支,你可能需要先将其拉取到本地,然后再切换到该分支。可以使用以下命令完成这个过程:

代码语言:javascript
复制
git switch --track <remote_branch_name>

这将拉取名为 <remote_branch_name> 的远程分支并创建一个与其关联的本地分支,并将当前工作目录切换到该分支。

4. 强制切换分支

如果在切换分支时存在未提交的更改,Git 默认情况下会阻止你切换分支。然而,有时你可能希望强制切换分支并放弃未提交的更改。可以使用以下命令:

代码语言:javascript
复制
git switch -f <branch_name>

这将强制将当前工作目录切换到名为 <branch_name> 的分支,并丢弃未提交的更改。

switch 和 checkout对比

git switchgit checkout 是在 Git 中用于切换分支的两个命令,它们有一些区别。以下是它们之间的主要区别:

  1. 操作方式不同: git switch 的操作方式更加直观和一致。它专门用于切换分支和创建新分支,更符合用户的直觉。而 git checkout 则具有更多的功能,可以用于切换分支、创建新分支、恢复文件等。
  2. 引起修改的情况不同: 在某些情况下,使用 git checkout 可能会导致未提交的更改被覆盖或丢失。例如,在切换分支之前,如果有对当前分支已修改但尚未提交的文件进行更改,那么 git checkout 会直接将这些更改应用到目标分支。这可能会导致不可预料的结果。相比之下,git switch 不会自动应用未提交的更改,它会提醒你先处理这些更改,然后再切换分支。
  3. 语义化的分支操作: git switch 的命令参数和选项更加语义化和直观。例如,使用 -c 选项来创建并切换到一个新分支,使用 --detach 选项来切换到一个游离的 HEAD(不指向任何分支)。这使得分支操作更加易于理解和记忆。
  4. 后续开发和推荐性: 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为最新的提交打标签:

image-20230801180555121
image-20230801180555121

使用命令git tag查看所有标签:

image-20230801180604450
image-20230801180604450

为历史提交打标签

之前某次提交忘记打标签了,为其打标签:

首先查看历史提交,git log --pretty=oneline --abbrev-commit,该命令的作用是以单行的形式显示提交历史,并使用缩略的提交哈希值。

image-20230801180901241
image-20230801180901241

为第一版打标签:

image-20230801180947942
image-20230801180947942

git show <tagname>查看标签信息:

image-20230801181012342
image-20230801181012342

删除标签

使用git tag -d tagname即可完成标签的删除;

image-20230801181125268
image-20230801181125268

远程仓库

远程仓库有很多,流行的主要就有github、gitee等,这里使用gitee来演示,创建账号和新建仓库具体操作就不演示了;

添加到远程仓库
  • 首先新建一个远程仓库

这里新建一个test_gitee的远程仓库;

image-20230730185232252
image-20230730185232252
  • 使用 git remote add 命令,并将 <remote_name> 替换为想要的远程仓库名称,<remote_url> 替换为远程仓库的 URL。使用git remote -v查看远程仓库详细信息:
image-20230730190416516
image-20230730190416516

这里是可以添加多个远程仓库的噢

  • 推送到远程仓库 使用 git push 命令将本地仓库中的分支推送到远程仓库。指定要推送的分支,以及远程仓库的名称和分支。例如,以下命令将本地 main 分支推送到名为 origin 的远程仓库。Copy Codegit push <remote_name> <branch_name> 如果是首次推送分支,可能需要使用 -u 参数来设置跟踪。例如: Copy Codegit push -u <remote_name> <branch_name> 如下,便完成了推送:
image-20230730194523198
image-20230730194523198
  • 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 分支。

在远程仓库中也可以查看到我们的提交了:

image-20230730194717690
image-20230730194717690
拉取远程仓库

从远程仓库中获取最新的代码更新是很重要的,就像从云盘上下载最新的文件到你的电脑一样。要拉取远程仓库的更新,需要执git pull操作:

git pull 命令的一般语法为:

代码语言:javascript
复制
git pull <远程仓库名> <远程分支名>

具体解释如下:

  • <远程仓库名>:指定要获取更新的远程仓库,通常是使用 origin 来表示默认远程仓库。
  • <远程分支名>:指定要获取更新的远程分支。

git pull 命令的执行过程大致如下:

  1. 首先,它会自动调用 git fetch 命令,从指定的远程仓库中获取最新的提交,但不会应用到本地分支。
  2. 然后,它会自动调用 git merge 命令,将获取的提交与当前分支进行合并。

在执行 git pull 命令时,可能会遇到以下情况:

  • 如果本地没有未提交的修改,git pull 会自动合并远程分支的更新到当前分支,并创建一个新的合并提交。
  • 如果本地有未提交的修改,git pull 默认会尝试自动合并。如果合并过程中发生冲突,你需要手动解决冲突后再提交。
  • 如果你想要强制执行 git pull,可以使用 git pull --force 命令。

另外,还有一些 git pull 命令的选项可以进一步控制其行为,例如:

  • --rebase:使用 rebase 而不是 merge 来合并远程分支的更新。
  • --no-commit:获取远程更新后不自动创建新的合并提交。
  • --ff-only:仅在快进合并的情况下才执行合并操作,否则终止。

如下,现在远程仓库的版本是第四次提交

现在新建一个分支并回退到第三版:

image-20230802182027080
image-20230802182027080

运行git pull命令没报错即拉取成功:

image-20230802182155328
image-20230802182155328
克隆远程仓库

在使用 git clone 命令进行克隆时,你有两种选择:

克隆到新建的项目目录:你可以指定一个新的项目目录,在该目录下执行 git clone 命令来克隆远程仓库。这将在指定的目录中创建一个新的项目,并将远程仓库的内容复制到该目录中。

例如:

代码语言:javascript
复制
git clone <远程仓库地址> <新项目目录>

在这种情况下,git clone 命令会自动创建一个与远程仓库同名的项目目录,并将远程仓库的内容复制到该目录中。

克隆到已存在的项目目录:如果你想将远程仓库的内容复制到一个已存在的项目目录中,可以直接进入该目录,并执行 git clone 命令。这将在当前目录中创建一个新的分支,并将远程仓库的内容复制到该分支中。

例如:

代码语言:javascript
复制
cd <已存在的项目目录>
git clone <远程仓库地址>

在这种情况下,git clone 命令会将远程仓库的内容复制到当前目录中,并自动创建一个新的默认分支。

  • 这里以很火的java开源博客系统halo为例:
image-20230802183333296
image-20230802183333296

注意:

默认情况下,git clone 命令会克隆远程仓库的所有分支。但是,克隆下来的分支在本地仓库中会以远程分支的形式存在,并不会自动创建与每个远程分支对应的本地分支。 你可以使用 git branch -r 命令查看克隆下来的所有远程分支,使用 git branch -a 命令查看所有本地分支和远程分支。

image-20230802183513370
image-20230802183513370

要将远程分支创建为本地分支,可以使用以下命令: git checkout -b <本地分支名> <远程仓库名/远程分支名> 这将创建一个新的本地分支,并将其设置为指定远程分支的跟踪分支。 另外,如果你只想克隆特定的分支而不是所有分支,可以使用 --single-branch 选项。例如: git clone --single-branch -b <分支名> <远程仓库地址> 这样只会克隆指定的分支,并忽略其他分支。

远程分支与标签操作

分支

创建远程分支并推送:要在本地创建一个新分支,并将其推送到远程仓库,可以使用以下命令:

代码语言:javascript
复制
git checkout -b <branch-name>
git push origin <branch-name>

这将创建一个名为<branch-name>的新分支,并将其推送到名为origin的远程仓库。

删除远程分支:要删除远程仓库中的分支,可以使用以下命令:

代码语言:javascript
复制
git push origin --delete <branch-name>

这将从远程仓库中删除名为<branch-name>的分支。请确保你有足够的权限来执行该操作。

查看远程分支:要查看远程仓库中的分支,可以使用以下命令:

代码语言:javascript
复制
git branch -r

这将显示远程仓库中的所有分支。

拉取远程分支:要将远程仓库的特定分支拉取到本地仓库,可以使用以下命令:

代码语言:javascript
复制
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>命令可以从远程仓库删除标签。

具体用法如下:

  1. <tag-name>表示你要删除的标签的名称。
  2. origin是远程仓库的名称,通常指向你的远程仓库URL。
  3. 在命令中使用冒号(:)来指示删除操作,冒号前为空,表示引用的空值。

举个例子,如果你想删除名为v1.0的标签,可以执行以下命令: Copy Codegit push origin :refs/tags/v1.0 执行命令后,Git会将该命令推送到远程仓库,远程仓库会删除对应的标签。 需要注意的是,这个命令只会删除远程仓库中的标签,而不会影响本地仓库中的标签。

Q&A

两本地仓库有一个同样的分支,同时推送到远程仓库会怎么样?

如果两个人的本地仓库都有一个同样的分支,并且同时推送到远程仓库,会导致冲突的发生。这是因为远程仓库不能直接处理两个相互冲突的提交。 具体情况如下:

  1. 假设两个人(Person A和Person B)都从远程仓库克隆了一个相同的分支,并在各自的本地仓库中进行了修改。
  2. Person A 先完成了修改并将其推送到远程仓库。
  3. 接下来,Person B 也希望将自己的修改推送到远程仓库。然而,由于此时远程仓库已经包含了 Person A 的提交,Person B 的推送会被拒绝,并且提示存在冲突。

在这种情况下,解决冲突的方法如下:

  1. Person B 需要先拉取最新的远程更新到本地仓库,使用 git pull 命令。
  2. git pull 命令会合并远程分支的更改到本地分支,并且可能触发冲突。
  3. 如果发生冲突,Person B 需要手动解决冲突。打开包含冲突的文件,根据标记手动编辑文件,解决冲突并保留需要的更改。
  4. 解决冲突后,使用 git add 命令将修改的文件标记为已解决冲突。
  5. 最后,使用 git commit 命令提交解决冲突后的更改。此时,会生成一个新的合并提交。
  6. 接下来,Person B 可以再次尝试推送自己的提交到远程仓库。

总之,如果两个人的本地仓库都有相同的分支,并且同时推送到远程仓库,会导致冲突的发生。在这种情况下,需要先拉取最新的远程更新,解决冲突后再推送修改到远程仓库。这样可以确保所有人的更改都能够合并,并保持代码的一致性。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-08-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 创建版本库
    • 新建版本库
      • 版本库基本概念
      • Git工作流程
      • 分支的基本概念
      • 标签的基本概念
      • Git基本操作
        • 提交文件到版本库
          • 常用查看版本库的命令
            • 更新版本并提交
              • 版本回退
                • reset/checkout的区别
                • git checkout
                • git reset
                • git reset拓展
              • 分支的使用
                • switch 和 checkout对比
              • 标签的使用
                • 远程仓库
                  • 添加到远程仓库
                  • 拉取远程仓库
                  • 克隆远程仓库
                  • 远程分支与标签操作
              • Q&A
                • 两本地仓库有一个同样的分支,同时推送到远程仓库会怎么样?
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档