首页 > 一文快速学懂常用工具——Git(下)
头像
蒋豆芽
编辑于 2021-07-26 10:38
+ 关注

一文快速学懂常用工具——Git(下)

img
  • 本专栏适合于软件开发的学生或人士,有一定的编程基础。
  • 本专栏针对面试题答案进行了优化,尽量做到好记、言简意赅。这才是一份面试题总结的正确打开方式。这样才方便背诵
  • 如专栏内容有错漏,欢迎在评论区指出或私聊我更改,一起学习,共同进步。
  • 相信大家都有着高尚的灵魂,请尊重我的知识产权,未经允许严禁各类机构和个人转载、传阅本专栏的内容。
img

欢迎关注蒋豆芽专栏:https://blog.nowcoder.net/jiangwenbo


一. Git 开始入门

终于要开始学习Git指令了,累死豆芽,其实Git指令相当简单,最好的方式就是边学习边实操。不过前面两节内容又不得不讲,这是为了构成知识的完整体系。

学习指令前,还要学点概念,嘿嘿。【不过可以先跳过,学会指令后再来看会理解更深刻。】

我们先来理解下 Git 工作区暂存区版本库概念:

  • 工作区:就是你在电脑里能看到的目录。

  • 暂存区:英文叫 stage 或 index。一般存放在 .git 目录下的 index 文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。

  • 版本库:工作区有一个隐藏目录 .git,这个不算工作区,而是 Git 的版本库。

下面这个图展示了工作区、版本库中的暂存区和版本库之间的关系:

img
图. 三个区的关系
  • 图中左侧为工作区,右侧为版本库。在版本库中标记为 "index" 的区域是暂存区(stage/index),标记为 "master" 的是 master 分支所代表的目录树。

  • 图中我们可以看出此时 "HEAD" 实际是指向 master 分支的一个"游标"。所以图示的命令中出现 HEAD 的地方可以用 master 来替换。

  • 图中的 objects 标识的区域为 Git 的对象库,实际位于 ".git/objects" 目录下,里面包含了创建的各种对象及内容。

  • 当对工作区修改(或新增)的文件执行 git add 命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。

  • 当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树。

  • 当执行 git reset HEAD 命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。

  • 当执行 git rm --cached <file> 命令时,会直接从暂存区删除文件,工作区则不做出改变。

  • 当执行 git checkout . 或者 git checkout -- <file> 命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区中的改动。

  • 当执行 git checkout HEAD . 或者 git checkout HEAD <file> 命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。

Git 文件的三种状态

img
图. Git 文件的三种状态

以上概念我们会在讲指令是具体讲解。


二. Git 指令学习

img
图. Git 指令

1. git init

Git 使用 git init 命令来初始化一个 Git 仓库,Git 的很多命令都需要在 Git 的仓库中运行,所以 git init 是使用 Git 的第一个命令。

在执行完成 git init 命令后,Git 仓库会生成一个 .git 目录,该目录包含了资源的所有元数据,其他的项目目录保持不变。

使用方法

使用当前目录作为Git仓库,我们只需使它初始化。【打开终端(Linux)或Git Bash(Windows)】

git init

该命令执行完后会在当前目录生成一个 .git 目录。

也可以使用我们指定目录作为Git仓库。

git init douya

初始化后,会在 douya 目录下会出现一个名为 .git 的目录,所有 Git 需要的数据和资源都存放在这个目录中。

我们来查看一下 .git 的目录。输入指令

ls

我们会发现没有输出什么信息,这是因为 .git 的目录针对用户隐藏的,目的为了防止误操作。

输入指令

ls -a

输出信息:

./  ../  .git/

这时我们就看到了隐藏目录,因为这个目录文件很重要,不建议初学者修改该目录或文件。

2. git clone

我们使用 git clone 从现有 Git 仓库中拷贝项目。之前我们通过Github创建了一个repo,接下来我们将这个repo拷贝到我们的目录下。我们复制一下仓库的地址:

img
图. 仓库的地址

输入命令:按照自己的【仓库名】和【地址】进行修改

git clone git@github.com:douya123456/git_practice.git

输出信息:

Cloning into 'git_practice'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.

可以看出来,已经拷贝成功,我们的目录下已经有了该仓库【工作目录】

切换至工作目录:

cd git_practice/

配置用户名和邮箱:

git config --global user.name "douya123456"
git config --global user.email "924510059@qq.com"

换成自己的【用户名】和【邮箱】。

3. git add 和 git status

我们来创建一个新文件 douya.c

touch douya.c

然后写入一行信息:

echo "hello douya!" >> douya.c

查看内容:

$ cat douya.c
hello douya!

可以看到已成功写入信息了。

接着我们将此修改添加至【暂存区】

git add douya.c

如果有多个地方修改,可以使用 git add .

然后我们使用 git status 命令查看暂存区状态

$ git status

On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   douya.c

可以看到这里 new file: douya.c ,说明这个新文件已经添加至【暂存区】,等待我们提交至【本地仓库】。

4. git commit 和 git log

git commit指令用于提交【暂存区】的修改至【本地仓库】中。

git commit -m [message]

[message] 可以是一些备注信息。

接下来我们就把暂存区的修改,提交至【本地仓库】。

输入

git commit -m "the first commit"

我们使用 git status 命令查看暂存区状态

$ git status

On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

可以看出来,我们已经成功将修改提交至【本地仓库】了。

【这里我们就要想想】:为什么区分【暂存区】和【本地仓库】?修改起来是不是更方便呢?答案是的。

接着我们通过git log命令查看我们的提交:

$ git log

commit 1ce338f343f906d6a60996ca89ff465e37ea5e4c (HEAD -> main)
Author: jiangwenbo <924510059@qq.com>
Date:   Sat Jul 24 15:31:09 2021 +0800

    the first commit

commit c714e05c5d8abe5b9e47978664ec187a5240510f (origin/main, origin/HEAD)
Author: douya123456 <52236573+douya123456@users.noreply.github.com>
Date:   Fri Jul 23 15:28:37 2021 +0800

    Initial commit

可以看到,连同我们的【提交备注】都一并可以在日志中查看到。

我们也可以只查看一个提交节点

$ git log -1

commit 1ce338f343f906d6a60996ca89ff465e37ea5e4c (HEAD -> main)
Author: jiangwenbo <924510059@qq.com>
Date:   Sat Jul 24 15:31:09 2021 +0800

    the first commit

git log -1意思是只查看最新的一个提交内容。

5. git rm

现在我们再添加一行信息至douya.c中:

echo "hello again!" >> douya.c

再次添加修改:

$ git add .
$ git status

On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   douya.c

现在我想把douya.c的修改从暂存区移除,因为我不想将这个修改提交至【本地仓库】

使用命令:

git rm --cached douya.c

当然我们也可以强行从暂存区和工作区中删除修改后的 douya.c 文件

git rm -f douya.c

使用这个-f命令后,工作区的 douya.c 文件也被删除了。

6. git mv

git mv 命令用于移动或重命名一个文件、目录或软连接。

git mv [file] [newfile]

如果新但文件名已经存在,但还是要重命名它,可以使用 -f 参数:

git mv -f [file] [newfile]

我们可以添加一个 README 文件(如果没有的话):

$ git add README 

然后对其重命名:

$ git mv README  README.md
$ ls
README.md

7. git push

git push 命用于从将本地的分支版本上传到远程并合并。

命令格式如下:

git push <远程主机名> <本地分支名>:<远程分支名>

如果本地分支名与远程分支名相同,则可以省略冒号:

git push <远程主机名> <本地分支名>

实例

以下命令将本地的 master 分支推送到 origin 主机的 master 分支。

$ git push origin master

相等于:

$ git push origin master:master

如果本地版本与远程版本有差异,但又要强制推送可以使用 --force 参数:

$ git push --force origin master

删除主机分支可以使用 --delete 参数,以下命令表示删除 origin 主机的 master 分支:

$ git push origin --delete master

所以我们将我们修改推送至【远端仓库】

$ git push origin main

Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (2/2), 194 bytes | 194.00 KiB/s, done.
Total 2 (delta 0), reused 1 (delta 0), pack-reused 0
To github.com:douya123456/git_practice.git
   fd34d5c..3ccc064  main -> main

注意:这里远端主机的名字,我这里是main,有的人是master,根据自己的情况填写。

8. git pull

对于远端服务器,有时候有更新,那么我们需要将远端的更新拉取到我们的【本地仓库】

git pull 命令用于从远程获取代码的更新并合至本地的版本。

git pull 其实就是 git fetch 和 git merge FETCH_HEAD 的简写。 命令格式如下:

git pull <远程主机名> <远程分支名>:<本地分支名>

实例

更新操作:

$ git pull
$ git pull origin

将远程主机 origin 的 master 分支拉取过来,与本地的 brantest 分支合并。

git pull origin master:brantest

如果远程分支是与当前分支合并,则冒号后面的部分可以省略。

git pull origin master

上面命令表示,取回 origin/master 分支,再与本地的 master 分支合并。

9. git fetch 和 git merge

git fetch 的作用是从远端服务器的某个分支获取最新更新到本地的仓库。

与git pull的不同是,git fetch 拉取分支至本地仓库后并不会自动合并,而是等待我们确认内容符合预期后,通过 git merge 手动合并【节点】。

比如我们拉取远端服务器的代码更新至本地的douya分支

git fetch origin master:douya

然后手动合并两个分支,douya向master方向合并。

git merge master douya

10. git branch

我们刚才通过git fetch拉取了一个分支,我们想查看它,可以使用命令

$ git branch -l

  douya
* main

-l表示local,查看本地分支,*表示正处于当前main这个分支

如果我们想查看远端的分支,可以使用命令:

$ git branch -r

  origin/HEAD -> origin/main
  origin/main

-r可以表示查看远端的分支。-a可以表示查看所有的分支。

我们还可以使用git branch 创建新分支。

如:

$ git branch new
$ git branch -l

  douya
* main
  new

上面一行命令可以看到,我们创建了一个 new 的新分支。

然后我们将其删除:

$ git branch -D new
$ git branch -l

  douya
* main

也可以删除服务器端的分支

$ git push origin :douya

上面一行命令用于是删除服务器端的douya分支

11. git checkout

checkout命令首先它可以创建分支,并切换至该分支:

$ git checkout -b new

Switched to a new branch 'new'

$ git branch -l

  douya
  main
* new

然后checkout命令可以用来切换分支:

$ git checkout douya

Switched to branch 'douya'

$ git branch -l

* douya
  main
  new

最后checkout命令的主要功能是撤销:

$ git checkout .

上面一行命令用于回退所有本地修改而未提交的文件内容。谨慎使用。

$ git checkout douya.c

上面一行命令用于将douya.c的文件修改内容全部回退

$ git checkout commit_id

上面一行命令用于回退至该提交节点commit_id

12. git blame

git blame <file> 以列表形式查看指定文件的历史修改记录。

13. git tag

如果你达到一个重要的阶段,并希望永远记住那个特别的提交快照,你可以使用 git tag 给它打上标签。

比如说,我们想为我们的 douya 项目发布一个"1.0"版本。 我们可以用 git tag -a v1.0 命令给最新一次提交打上(HEAD)"v1.0"的标签。

-a 选项意为"创建一个带注解的标签"。 不用 -a 选项也可以执行的,但它不会记录这标签是啥时候打的,谁打的,也不会让你添加个标签的注解。 我推荐一直创建带注解的标签。

$ git tag -a v1.0 

当你执行 git tag -a 命令时,Git 会打开你的编辑器,让你写一句标签注解,就像你给提交写注解一样。

现在,注意当我们执行 git log --decorate 时,我们可以看到我们的标签了。

14. git 小结

想要学习更多的git内容,请访问官方学习网址:https://git-scm.com/book/zh/v2/

Git指令速查手册:https://www.runoob.com/manual/github-git-cheat-sheet.pdf


三. 节点合并的原理

1. git merge命令工作流程

git merge命令用于从一个分支到另一个分支的合并。

假设下面的历史节点存在,并且当前所在的分支为“master”:

img
图. master

那么git merge topic命令将会把在master分支上二者共同的节点(E节点)【之后分离的节点(即topic分支的A B C节点)】重现在master分支上,直到topic分支当前的commit节点(C节点),并位于master分支的顶部。最后沿着master分支和topic分支创建一个记录合并结果的【新节点】,该节点带有用户描述合并变化的信息。

即下图中的H节点,C节点和G节点都是H节点的父节点。

img
图. master

一定要弄清楚合并的到底是什么?合并的是【提交节点】。而【提交节点】包含的各种【修改】也会跟着合并入master主分支中。

2. git merge命令实例说明

  • 创建两个分支main 和 douya

     $ git branch -l
    
     * douya
       main
  • 查看两个分支的提交节点

    douya分支

     $ git checkout douya
     $ git log
    
     commit ad137803ad275713353c08ebcbe6d5d109839a28 (HEAD -> douya)
     Author: douya123456 <924510059@qq.com>
     Date:   Sat Jul 24 21:09:11 2021 +0800
    
         444
    
     commit 32274bc40ae55872506411ebe8d7b18d37b3ce00 (origin/main, origin/HEAD)
     Author: douya123456 <924510059@qq.com>
     Date:   Sat Jul 24 16:54:59 2021 +0800
    
         11
    
     commit 9e237fe95045e619f56472f882e6f20f348da5e0
     Author: douya123456 <924510059@qq.com>
     Date:   Sat Jul 24 16:52:02 2021 +0800
    
         douya

    main分支

     $ git log
    
     commit dd0badc3ba6568772fa9d1a463001b99433a4afd (HEAD -> main)
     Author: douya123456 <924510059@qq.com>
     Date:   Sat Jul 24 21:26:24 2021 +0800
    
         666
    
     commit 597b931fd7314cb1d72de356342defd4f1a1055f (HEAD -> main)
     Author: douya123456 <924510059@qq.com>
     Date:   Sat Jul 24 17:07:40 2021 +0800
    
         22
    
     commit 32274bc40ae55872506411ebe8d7b18d37b3ce00 (origin/main, origin/HEAD)
     Author: douya123456 <924510059@qq.com>
     Date:   Sat Jul 24 16:54:59 2021 +0800
    
         11
    
     commit 9e237fe95045e619f56472f882e6f20f348da5e0
     Author: douya123456 <924510059@qq.com>
     Date:   Sat Jul 24 16:52:02 2021 +0800
    
         douya

    可以发现,两个分支共同的节点为“11”,那么douya分支后面的“444”节点将重现与main分支上,并且成为顶点,两个分支还会合并出一个新的节点。

  • 开始合并

    我们在main分支下,将douya分支合并至main上:

     $ git merge main douya

    显示合并成功,我们查看log:

     $ git log
    
     commit 2c8667fecffc4fcc92dbee9a81355df463d7a476 (HEAD -> main)
     Merge: dd0badc ad13780
     Author: douya123456 <924510059@qq.com>
     Date:   Sat Jul 24 21:27:20 2021 +0800
    
         Merge branch 'douya' into main
    
     commit dd0badc3ba6568772fa9d1a463001b99433a4afd
     Author: douya123456 <924510059@qq.com>
     Date:   Sat Jul 24 21:26:24 2021 +0800
    
         666
    
     commit ad137803ad275713353c08ebcbe6d5d109839a28 (douya)
     Author: douya123456 <924510059@qq.com>
     Date:   Sat Jul 24 21:09:11 2021 +0800
    
         444
    
     commit 597b931fd7314cb1d72de356342defd4f1a1055f
     Author: douya123456 <924510059@qq.com>
     Date:   Sat Jul 24 17:07:40 2021 +0800
    
         22
    
     commit 32274bc40ae55872506411ebe8d7b18d37b3ce00 (origin/main, origin/HEAD)
     Author: douya123456 <924510059@qq.com>
     Date:   Sat Jul 24 16:54:59 2021 +0800
    
         11
    
     commit 9e237fe95045e619f56472f882e6f20f348da5e0
     Author: douya123456 <924510059@qq.com>
     Date:   Sat Jul 24 16:52:02 2021 +0800
    
         douya

    可以看到,douya分支的节点重现于main分支上,而且两个分支合并了一个新的节点,修改也包含于main分支中了。


四. 常考面试题

  1. git branch 创建分支和 git checkout -b 创建分支有什么区别?⭐⭐⭐

    git branch 创建后不会切换至新分支;而git checkout -b 创建后会切换至新分支

  2. git pull 和 git fetch 有什么区别?⭐⭐⭐

    git pull 拉取远端服务器更新内容后自动与当前分支合并;而git fetch 拉取远端服务器更新内容后不与当前分支合并,需要手动merge

更多模拟面试

全部评论

(1) 回帖
加载中...
话题 回帖

推荐话题

相关热帖

近期热帖

近期精华帖

热门推荐