Git 的基本使用

Git 的基本使用

什么是 Git

​ 在使用 Git 前,了解 Git 对于之后的学习会有很大的帮助。

​ Git 是一个分布式的版本控制软件,最初的目的是为了更好的管理 Linux 内核的开发。相比较于其它的软件版本控制系统,虽然总体上用起来与它们十分的相似,但是在对于信息的存储和认知上有很大的差异。主要体现在:

  • 直接记录每个文件的快照,而不是进行差异的比较

    • 对于其它的软件版本控制系统,如 SVN,在每个软件版本中都是通过记录与最初文件的差异来进行版本控制的。而 Git 则不同,每个版本都是通过创建之前版本文件的一个快照,这有点类似与一个小型的文件系统,当然,为了效率,Git 在每个版本中对于没有修改的文件使用一个链接指向之前的文件。因此当每次切换版本时,你都会感觉到非常快。
  • 几乎所有的操作都在本地执行

    • Git 是一个分布式的软件版本控制软件,在你自己本地也可以存储对应的文件,这样极大地提高了系统的容错性和容灾性。在你每次提交时,都是只会访问自己本地的文件系统,只有当你希望将自己的软件版本推送到远程服务器时,此时才需要访问互联网。
  • Git 可以保证完整性

    • Git 中的所有数据在存储前都会计算校验和,然后通过校验和来引用对应的版本。因此在文件有任何改动的情况下,Git 都能立即发现。

    • 在每次提交后,Git 都会有一个校验和来引用对应的版本,因此在你执行提交之后,很难导致数据的丢失。

Git 的几种状态

​ 理解 Git 的几种状态对于学习是极其重要的,Git 总共分为三种状态:***已修改(modified)***、***已暂存(staged)***、***已提交(committed)***。

  • 已修改(modified) 表示已经修改了 工作区 的文件,但是还没有提交。
  • 已暂存(staged)表示已经将修改的文件放入了 暂存区,使之包含在下次提交的快照中
  • 已提交(committed)表示已经将暂存区的快照文件放入了 本地仓库

使用图来表示可能会直观一些:
在这里插入图片描述

请牢记这三种状态和这三个区,这是 Git 的核心。

一般的 Git 流程如下:

  1. 在工作区内修改了文件,此时 Git 的状态更新为 已修改
  2. 将修改后的文件加入暂存区,此时 Git 的状态为 已提交
  3. 提交更新,将暂存区内的快照文件存入本地数据库,此时 Git 的状态为 已提交

Git 的使用

运行前的一些配置

​ 在使用 Git 前,需要配置一些相关的变量,如用户名、邮箱、默认文本编辑器等,只有配置了这些必须的属性,Git 才能正常使用。当然,Git 还有一些其它的配置信息,可以通过 git config --list --show-origin 来查看相关的配置属性以及所在的位置,但是 用户名、邮箱是必须配置的,因为 Git 在每次提交时都会将这些信息写入,并且这是不可更改的。

​ 有两种方式可以设置这些属性:一是通过 git config --global user.name xxxxgit config --global user.emal xxxx@xx.com ;二是通过直接修改对应的配置文件。

​ 这里以命令的方式进行配置为例:

# 配置用户名
git config --global user.name FatalFlower
# 配置用户邮箱
git config --global user.email GuiHuaLinked@gmail.com

默认文本编辑器的设置,当 Git 需要输入信息时将会调用它,如果没有配置的话,Git 会调用默认的文本编辑器。

# 设置 Git 的文本编辑器为 emacs
git config --global core.editor emacs

当在 Windows 上修改 Git 默认的文本编辑器时,需要指定执行程序的全路径。

基本使用
  1. 初始化 或 clone

    通过初始化的方式或者 clone 的方式都可以得到一个 Git 仓库。

    • 初始化一个 Git 仓库(此时你应当已经进入了对应的项目目录)

      # 初始化一个本地 Git 仓库
      git init
      

      ​ 这会在你的项目目录下创建一个 .git 文件夹,这是一个隐藏的文件夹,里面包含了 Git 仓库所必须的一些文件。

    • clone 一个 Git 仓库

      git clone https://github.com/LiuXianghai-coder/tourism.git
      

      ​ 这会将整个 tourism 仓库克隆到本地,同时还会将该仓库带有的 .git 文件夹 clone下来,得到整个项目的提交历史和文件等其它的信息。

  2. 修改工作区的内容

    ​ 在工作区(即你能够看到的项目目录中),对其中任意几个文件进行修改,此时 Git 会检测到文件的修改。如果在修改完几个文件之后运行 git status 可以看到当前的状态。看起来可能像下图这样:

IAO8yC.md.png
​ 此时的修改后的文件依旧存储在 工作区

  1. 将工作区的内容添加到暂存区

    ​ 此时可以通过 git add *.txt 命令将当前工作目录下所有的 txt 文件都放入暂存区。再通过 git status 命令查看 Git 当前的状态。可能与下图类似:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zWniBMsk-1628508096776)(https://s3.jpg.cm/2021/08/09/IAOX7t.md.png)]

    ​ 注意此时修改后的文件处于 暂存区

  2. 提交暂存区内的文件快照

    ​ 此时暂存区内有修改后的文件的快照,在未提交之前,这些快照依旧时不稳定的,因为它依旧有可能会丢失,因此最好的做法是将它们提交到本地仓库。

    ​ 使用 git commit 命令可以将暂存区内的快照提交到本地仓库,此时提交将会强制要求输入本次提交的信息,也可以在命令后加上 -m 选项来输入本次的提交信息。但是一般来讲,还是建议使用 git commit 不带上 -m 选项,因为这会使得你能够更加详细地说明本次提交地内容。

    ​ 执行完 git commit 命令,并且输入对应的提交信息后,再次运行 git status 命令查看 Git 的状态,可以看到工作区是干净的。

    IAOcSR.png

  3. 推送到远程服务器

    ​ 之前讲过,Git 是一个分布式的软件版本控制工具,它的分布式就体现在这,客户端和服务端以及拉取这个仓库的客户端都会有这个仓库的 .git 文件夹。也就是说,以上这几个端都有对应的副本,因此对于软件版本控制来讲是极其安全的。只要即使拉取和推送了对应的仓库。

    ​ 要推送本地的仓库到远程服务端,首先要添加它的上游分支:

    # 在这里我将自己的一个 Github 仓库放到这里作为一个上游
    git remote add origin https://github.com/LiuXianghai-coder/Test-Repo.git
    

    ​ 此时通过 git remote --verbose 可以查看存在那些上游分支:

    IAOuPu.md.png

    ​ 有了上游分支后我们就可以将我们本地的仓库推送到上游服务器上了 :)

    ​ 使用 git push 命令可以将我们的仓库推送上去,具体命令如下:

    # 这里需要注意的是,origin 是指定的上游分支名称,origin 是默认的一个上游分支,具体名称需要参考 `git remote --verbose` 命令下输入的上游名
    
    # main 是上游的一个分支,其实 git 在这里做了一些简化,实际运行的命令是 `git push origin master:master` 将本地的 master 分支推送到上游 origin 的 master 分支。
    
    # 具体的运行内容为 `git push 上游服务器名 本地分支:远程仓库分支`,如果远程仓库的分支名不存在,则会创建一个分支。
    git push origin master
    

    之后会要求输入用户名和密码,按照要求输入对应信息即可。

    此时再查看对应的远程仓库,可以看到我们的文件已经被推送上去了。

    如果你使提交的仓库不是一个空的仓库,那么可能会出现 类似以下的错误:

    IAO1uG.md.png

    这是由于两个存储库之间没有公共的祖先,因此导致合并失败。

    可选的解决方法是将两个存储库先在本地合并,然后再提交回去:

    # origin 和 master 需要换成自己对应的上游和分支
    # 使用 --allow-unrelated-histories 允许两个没有关联祖先节点的分支进行合并
    git merge origin/master --allow-unrelated-histories
    
    # 之后,再次提交即可
    git push origin master
    
    1. Git 提交日志

    ​ 上文提到,Git 地每次提交都会创建一个新的快照,同时将校验和作为一个历史提交目录。因此,在每次提交之后,Git 都会带有对应的快照目录,之后就很难在丢失已经提交的文件了。

    ​ Git 提供了一个命令用于查看该仓库的提交日志:git log。该命令会默认按照提交历史从后往前输出对应的提交日志:

    image

    可以查看到最近的几次提交日志。跟在 commit 后面的便是那次提交的校验和。

    可能这么看起来不是特别舒服,试试 git log --pretty=oneline --graph

    --pretty 可以设置输出格式,online 代表只输出一行,即带 SHA 校验和的那一部分。--graph 选项则是更加直观地查看提交信息,注意分支合并的那部分。

    --pretty 可以自自定义输出格式,以下面的命令为例:

    # format 表示自定义格式化输出,%h 表示输出简写的校验和(一般情况下,Git 通过前8为校验和就能得到对应的提交文件目录),%an 表示作者名字,%ar 表示修订日期,%s 表示提交说明。
    git log --pretty=format:"%h - %an, %ar : %s"
    

    具体的格式如下图所示:

    ​ 使用自定义格式与 --graph 选项配合使用往往时一个很不错的选择。

    ​ Git 的每次提交都会附带每次提交发生的一些变化,可以通过 -p 选项(即 --patch)来得到这些信息。由于这样的话输出可能会很长,因此可以使用 -<n> 选项(n 在这指的是输出的记录条数)限制输出日志的数量。

    # 打印出最近的两次提交的补丁信息
    git log -p -2
    

    ​ 除了使用 -<n> 选项限制输出条目数外,使用 --since--until 的组合方式来获取指定时间区段也是一个很不错的选择。--grep 过滤那些提交说明与指定正则表达式匹配的提交。--author 选项则只显示作者名与输入的作者名相匹配的提交。

    ​ 合理利用这些选项,这会给你带来很大的帮助。

  4. 为提交打上标签

    ​ 有时可能再提交某个软件版本时希望加上相对应的标签,以表示对应的版本信息等,使用 git tag 命令可以有效地解决这个问题。

    ​ Git 有两种标签:轻量标签(lightweight)附注标签(annotated)

    • 轻量标签

      • 轻量标签本质上只是将提交校验和存储到一个文件中,没有保存任何其它的信息。创建轻量标签很简单,只需要在 git tag 命令后加上对应的标签名即可。

        # 为当前分支打上 v1.0-lw 的轻量标签
        git tag v1.0-lw
        
      • 使用 git tag --list 可以查看对应的标签

      • 如果希望通过标签名来检出相对应的提交,可以使用一般的正则表达式匹配符来匹配。如 git tag --list "v1.0*" 即可检出所有标签以 v1.0 开头的所有提交。

    • 附注标签

      • 附注标签会包含许多的额外信息,包括打标签的人的名字、邮件地址、日期时间,以及该标签对应的信息。
      • 创建一个附注标签需要加上 -a 选项,即 “annotate”。同时也可以通过 -m 选项添加一条标签对应的信息。与提交一样,如果没有指定 -m 选项,Git 会启动指定的编辑器提示输入标签信息。
    • 可以通过 git show <tag> 来查看对应的标签和对应的提交信息

      # 这里 v1.9.1 是我自己给一个提交添加了一个附注标签
      git show v1.9.1
      

      可能得到的结果如下所示:

      如果将该命令运行在轻量标签上,则只会看到对应的提交信息而看不到标签的相关信息。

    • 如果想要对某个特定的提交添加标签信息,那么只需要在添加标签的命令后加上对应的提交校验和即可。这是因为校验和是一个提交的引用,因此对应的提交校验和就相当于是一个提交的引用。可以通过上文的 git log 命令查看相关的提交历史记录从而得到对应的校验和。

      # 这里给 720e86018f33410ef12f0bfbf7911e37f07dfccf 提交添加 v1.9.2 的附注标签。轻量标签只需去掉 -a 选项即可。
      git tag -a v1.9.2 720e86018f33410ef12f0bfbf7911e37f07dfccf
      
    • 共享标签

      ​ 默认情况下,Git 在推送时并不会将打好的标签一起推送过去,因此在创建完标签之后必须手动将标签推送到远程仓库上。这个与上传分支有点类似:git push origin v1.9.0 这里的 origin 是上文提到的上游,v1.9.0 是要提交的标签。

      ​ 如果要一次性提交多个标签,可以使用 git push origin --tags 这会把所有不在远程仓库的标签都提交到远程仓库。这里提交的标签是所有的标签,包括轻量标签和附注标签。在一次性批量提交的情况下,Git 没有办法区分轻量标签和附注标签。

    • 删除标签

      ​ 如果要删除本地标签,可以使用 git tag -d <tagName> 来删除指定的标签。

      ​ 如果要删除远程仓库上的标签,一般有两种方式:

      • 一是推送一个空值到远程仓库的标签,从而替换掉它

        # 推送一个空值到远程分支的对应标签,也就相当于删除了之前对应的标签
        git push <remote> :refs/tags/<tagName>
        
      • 二是直接发送一个删除标签的命令,显示地删除它。

        # 推送到上游一个删除标签的命令,显示地删除它
        git push <remote> --delete <tagName>
        

文件版本的管理

​ 上文提到,Git 对每个提交都保存着一个对应的文件版本,通过生成的校验和来作为该次提交的文件版本的引用。一般来讲,有时可能需要回滚到之前的版本,有时可能需要在一个现有的文件版本上进行开发,或者有时需要将多个文件版本进行合并。这些功能 Git 都能帮助我们很好地完成。

  • 文件版本的回滚

    ​ 根据前文的介绍,使用 git log 命令可以查看最近的历史提交以及相关的检索,同时也介绍到得到的校验和是一个文件版本的引用。因此,通过 git log 命令可以很轻松地实现文件版本地回滚操作。

    ​ 使用 git reset 可以更新相关的三个区,还记得上文提到到三个核心区域吗?git reset 有三个常用的选项,分别对应三个不同的区:

    • git reset --soft <tree-ish> 这里的 tree-ish 表示的是之前提到过的文件校验和

      ​ 使用这个命令将将 HEAD 指针(在存储库中)将其移动到 tree-ish 对应的版本。注意,此时的暂存区和工作区依旧保存着之前内容。因此此时运行 git status 你将会看到是出于一个未提交的状态。

      ​ 使用以下的图片来描述可能会更加直观一些:

      ​ 完成提交之后的三个区域的内容:

      ​ 此时执行 git reset --soft a7d5c77 (之前讲过,一般文件管理八位的校验和 Git 就可以找到对应的引用文件版本),将会使得 HEAD 指针移动到 a7d5c77 的文件版本。此时看起来像这样:
      在这里插入图片描述

      ​ 这样的话,修改暂存区的内容再次提交就相当于取消了上次的提交,然后更换为了新的提交。(事实上,将这一操作理解为在仓库中根据上一版本新建了一个分支可能更为合理一些)。这也是 git commit --amend(修改最后一次提交)的工作原理。

    • git reset --mixed <tree-ish>

      ​ 使用 --mixed 选项会将暂存区的内容修改为tree-ish 版本的内容,同时也会更新 HEAD 指针到 tree-ish。如果此时运行 git status 查看状态的话,就会发现当前的状态处于 未添加 状态。这是由于工作区的内容依旧未发生改变,因此 Git 检测这两个区域的内容不一致所导致的。

      ​ 依旧以上文的版本为例,执行 git reset --mixed a7d5c77 后三个区域的内容如下所示:

    • git reset --hard <tree-ish>

      ​ 前文提到过,尽量使得你的 Git 状态时干净的。这是因为只有在提交创建对应的快照之后才能保证文件版本的稳定性。但是相反,在未提交之前,所有的文件的改动都是有可能丢失的。比如说在使用 --hard 的选项的条件下,如果你的工作区内含有未提交的文件,那么这么做之后将会丢失所有你未提交的改动文件,因此,在执行这个命令之前,务必确保你的 Git 状态时干净的。

      ​ 使用 --hard 选项会将三个区的所有内容都更新到指定的 tree-ish 版本。依旧以上文为例,此时的三个区的状态看起来如下图所示:

    • 使用 git reset 会导致相关的日志的变化,如果此时回到的是最初的提交,那么就无法看到之后的提交校验和。此时需要使用 git reflog 来查找对应的校验和,然后再通过 git reset 更新回去。这更加说明时刻保持工作区干净的重要性,只要将工作区的内容提交,那么数据就不会丢失。

  • 分支的管理

    ​ 前面简要介绍了一下有关 Git 的分支信息。在一个软件的开发过程中,由于需求的不确定性,有时往往需要推出好几个版本。Git 的分支就很适合完成这件事。

    • 分支的创建

      • 创建分支的命令

        # 创建 main 分支
        git branch main
        
        # 将当前的分支切换到 main
        git checkout main
        
        # 或者,你可以一次性的执行完这两个命令。通过 git checkout 添加 -b 选项即可在切换指定的分支不存在时创建分支
        git checkout -b main
        

        此时执行 git branch 查看当前的分支信息,你可以看到当前的分支是位于 main 分支。

      • 创建远程分支

        ​ 前面提到过推送本地仓库时将其推送到对应对应的远程服务器分支,如果当前的分支不存在则会创建对应的分支,使用这个方法可以很容易实现远程仓库的分支创建。这相当与一个简化的推送命令。

        # 将当前的分支切换到 test
        git checkout -b test
        
        # 将当前的分支推送到 origin 上游 test 分支,由于上游现在不存在 test 分支,因此这会自动在 origin 上创建一个 test 分支
        git push origin test
        
        # 这相当于执行了以下命令 git push <remote> source:target。即将本地的 source 分支推送到 remote 的 target 分支。同样的,当 remote 的 target 分支不存在时,会自动创建 target 分支。
        # 因此上面的推送命令与下面的推送命令等效
        git push origin test:test
        
        # 如果想将本地的分支推送到远程服务器的不同分支,只需改一下 target 分支即可。
        # 这里将本地的 test 分支推送到远程服务器的 master 分支
        git push origin test:master
        
      • 跟踪分支

        ​ 从远程仓库克隆的存储库可能包含很多的分支,但是克隆的时候只会克隆默认的分支(一般是 master)。如果想要直接跟踪对应的远程分支,这也是很简单的。

        ​ 首先,查看对应的远程仓库信息,运行 git remote show <remote> 显示对应的远程仓库的信息。这里以 center 仓库为例。

        # 查看远程仓库 center 的详细信息
        git remote show center
        

        可能会看到如下的输出:

        现在,跟踪 center 仓库的 main 分支

        # 这会在本地仓库中自动创建一个 main 分支用于跟踪 center 远程仓库 main 分支
        git checkout --track center/main
        
        # 上面的命令还可以简化一下
        git checkout main # 由于本地并没有 main 分支,因此 Git 首先会检测远程仓库是否有该分支,如果有该分支,那么 Git 将会自动创建该远程仓库对应的分支的跟踪。
        
        # 如果想要使用一个不同的分支名字来跟踪 center 仓库的 main 分支,可以这样做
        git checkout -b main2 center/main # 在本地创建一个 main2 分支用于跟踪远程仓库的 main 分支
        
        # 如果想要切换本地的 master 分支去跟踪其它的上游分支,可以通过以下命令来实现
        # git branch -u <remote>/<remoteBranch> <localBranch>
        # 注意 git 中的 -u 选项一般指 --set-upstream 即设置上游
        # 这里将本地的 master 分支用于跟踪 orign 上游下的 master 分支
        git branch -u origin/master master
        
    • 分支的合并

      • 简单的合并

        以上文为例,现在将 main 分支同 test 分支合并。

        # 由于这两个分支包含的所有文件的文件名不一样,因此在这里不会出现合并冲突的问题
        # 此外,如果这两个分支没有公共的祖先节点,如果你很明白你在做什么,请记得加上  --allow-unrelated-histories 选项
        git merge main test
        
      • 合并冲突的解决

        ​ 现在使用 git reset 使 main 分支 和 test 分支回到合并之前的节点。然后在这两个分支里都创建一个名为 combine.txt 的文件,然后在两个分支分别写入不同的内容,然后提交。

        ​ 在执行之前的合并命令,你会看到类似以下的输出:

        ​ 由于两个分支当前版本都含有combine.txt 文件,并且文件的内容有冲突,因此 Git 的自动合并失败了。此时查看 combine.txt 文件,会看到类似下面的输出:

        ​ (合并时冲突的文件)

        ​ 此时使用 git status 可以查看所有合并冲突的文件。

        ​ 解决冲突文件的一般做法是在两个分支文件的内容中保留其中一个分支的内容,当然你可以自己手动修改它们。

        ​ 如果希望使用合并工具来解决冲突的话,git mergetool 是一个很好的选择。在 Linux 下一般默认将 vimdiff 作为合并解决工具,可以通过添加 -t <tool> 来指定合并解决工具,可以通过 git mergetool --tool-help 来查看那些合并解决工具是可用的,但是前提是你必须现在你的计算机上安装好它。

        ​ 处理好之后,再执行提交即可。

        ​ 值得注意的是,使用合并工具解决冲突后会保留之前冲突的文件的副本,你可以保留它并将它提交放入本地仓库,或者也可以直接删除它。

      • 变基的使用与建议

        ​ 使用变基可以使得你的提交看起来是线性的,在单人开发的情况下不会出现什么问题,但是对于一个软件开发来讲,这是不可能的。由于多人一起协同开发时使用变基带来的一些令人头痛的问题,我在此强烈建议开发情况下不要使用变基合并分支,所以对于变基的使用将只是简要介绍它的工作原理。

        ​ 与 merge 不同,merge 是将两个分支的内容直接进行合并,遇到冲突时提示对应的信息。而如果使用变基来合并分支时,首先它会找到要合并的目标分支的共同公共祖先节点,然后将当前在公共祖先节点添加的补丁在添回到目标分支,然后将当前的分支的父提交指向目标分支。因此这么一顿操作后,当前的分支就直接添加到目标分支的后面,使其看起来像一条直线。

        # 将当前的分支变基到 master 分支,再次建议,不要在多人开发的情况下使用变基
        git rebase master
        

以上就是有关 Git 的一些基本使用,希望它对你有帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值