跳过正文
  1. 文章/

Git掌握与实践【一】

··3613 字·17 分钟
代码 Git
目录
Git掌握与实践 - 系列文章
§ : 本文

1. Git基础
#

1.1 为什么要用Git?什么是Git?
#

当你想要使用Git的时候,你一定遇到了版本控制相关的问题。

比如:想要修改A.txt,但是又担心现在的A.txt文件内容仍然需要使用。一些人会选择复制一份A.txt为A_1.txt的,再去修改A_1.txt。长此以往,你可能有了A_99.txt、A_100.txt,并且过了一段时间,你已经不知道哪一文件夹修改了哪一部分。这个时候,点开Git教程,它会给你答案。

再比如:你有一个A.txt需要给你的同事甲协作,让他修改部分内容,同时,你也要修改A.txt。那情况就更麻烦了,如果没有合适的工具,一些人会选择把同事修改完的命名为A_甲_1.txt,再把自己修改的命名为A_1.txt,然后同时打开这两个文件,手动对比区别,最后归纳为A_2.txt。这个时候,你想到之后还要很多次进行一样的操作,太耗费时间,点开Git教程,它会给你答案。

Git是一个分布式版本控制系统。

Git就像一个强大的时间机器,它能记录你项目的所有修改。想象一下,你正在写一篇文章,每修改一个段落,Git都会帮你保存一个快照。这样,你可以随时回到任何一个历史版本,比较不同版本之间的差异,甚至可以把几个人的修改合并到一起。简单来说,Git是一个让你安全、高效地管理项目代码的工具,它能帮你追踪修改、方便协作、并随时回到过去。

下面是版本控制系统的对比和你选择Git的理由:

特性/对比项GitSVN (Subversion)Mercurial (Hg)CVS
架构分布式 (Distributed)集中式 (Centralized)分布式 (Distributed)集中式 (Centralized)
分支管理强大且灵活,轻量级,鼓励频繁使用相对笨重,创建分支成本高较好,与 Git 类似较弱,不鼓励频繁使用
性能非常快,尤其在大型项目和大量提交时较慢,性能瓶颈明显相对较快,但不如 Git性能较差,尤其在大型项目时
离线工作支持,本地有完整仓库不支持,依赖中央服务器支持,本地有完整仓库不支持,依赖中央服务器
资源占用占用磁盘空间较多,但操作高效占用磁盘空间较少,但操作相对较慢占用磁盘空间较多,但操作高效占用磁盘空间较少,但操作相对较慢
复杂性学习曲线稍陡峭,但功能强大相对简单易学,但功能较少学习曲线较平缓,功能类似 Git较为简单,但功能较少
流行度目前是行业标准,非常流行逐渐被 Git 取代,但仍有部分应用受欢迎程度不及 Git已经过时,不推荐使用

选择 Git 的理由个人开发者团队开发者
优势- 版本控制的强大能力,方便回溯- 高效协作,多人同时开发
- 本地操作,离线也能工作- 强大的分支管理,灵活应对复杂需求
- 方便实验新想法,随时回滚- 代码审查,提高代码质量
- 保护代码,防止误删和丢失- 轻松处理合并冲突,避免开发混乱
- 学习现代开发标准,提高竞争力- 广泛的社区支持,易于解决问题
- 为协作开发打下基础,方便未来团队合作- 支持多种开发模式(如 Gitflow)
总结更有效率地管理个人项目,保护代码资产提高团队协作效率,确保项目高质量和快速交付

1.2 Git安装
#

请自行搜索:“怎么在xxx系统安装Git”。

2. Git基础使用
#

以下以Linux环境为例。

让我们开始,新建文件夹 learn_git,并进入该文件夹:

mkdir learn_git
cd learn_git

以后我们的操作都在 learn_git文件夹下进行。

2.1 Git基础配置
#

在正式使用Git创建并管理项目前,我们还需要对Git进行初始的配置。Git提供了 git config工具,来帮助我们进行配置。在终端输入:

git config --list --show-origin

我们可以查看所有的配置以及它们所在的文件信息。

终端中输出了配置文件名称、路径以及详细的配置项。Git的配置文件总共有三个,分别存储在不同的地方,并对应不同的权限(优先级:系统>用户>仓库)。

  1. 存储在安装目录下 etc路径下的 gitconfig文件,它是系统全局配置文件,它包含系统上每一个用户及他们仓库的通用配置。
  2. 在当前系统用户下的 .gitconfig文件,这是当前用户的全局配置文件,它存储了仓库都共享的通用配置选项。
  3. 存储在仓库目录下的 .git/config文件,是针对仓库的配置文件,它存储了仓库的配置信息。

如果你安装了 vscode,你可以设置 git的默认编辑器为 vscode

git config --global core.editor "code --wait”

2.2 Git基础概念
#

2.2.1 三个功能区域
#

graph LR A[工作区] -->|git add| B(暂存区); B -->|git commit| C[版本库]; C -->|检测追踪项目文件| A
  1. 工作区(Working Directory)
    • 工作区是指包含项目代码的本地目录,是我们平常在编辑器中修改和操作的目录。
  2. 暂存区(Stage/Index)
    • 暂存区是用于存储即将被提交到版本库中的文件快照。我们可以多次预览和审查文件是否正确修改。
  3. 版本库(Repository)
    • 版本库可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
2.2.2 三种文件状态
#
  1. 已修改(modified):
    • 表示文件已被修改但尚未被暂存。我们在工作区修改了文件,并点击了保存,这时候文件状态会变成已修改。也就是说,你已经对文件进行了更改,但还没有使用 git add命令将其添加到暂存区域。
  2. 已暂存(staged):
    • 表示对一个已修改文件的当前版本做出了标记,以便在下一次提交时将其纳入版本控制。也就是说,使用 git add命令将修改的文件添加到暂存区域中。
  3. 已提交(committed):
    • 表示数据已经被安全地保存在本地数据库中。也就是说,在你执行了 git commit命令之后,所有的更改都被保存到了 Git 仓库的历史记录中。

2.2.3 .git文件夹简单介绍
#

当我们执行了 git init初始化仓库之后,Git 会创建一个 .git文件夹,下面是部分文件信息

  1. HEAD:指向当前活动分支的指针。
  2. config:版本库的配置文件,包括用户名、邮件地址、编辑器等信息。
  3. description:用于在 GitWeb 等工具中显示有关版本库的描述信息。
  4. hooks/:包含可自定义的 Git 钩子脚本,用于实现特定功能或执行自动化任务。
  5. objects/:包含 Git 对象数据库,其中存储了版本库中所有的文件和提交历史记录。
  6. refs/:包含分支和标签的指针文件,其中保存了每个分支和标签及其所指向的提交 ID。
  7. index:暂存区的索引文件,用于记录下一次提交要包括的文件。

2.3 学习掌握
#

2.3.1 创建一个版本库并提交第一个文件
#

在已经新建文件夹 learn_git并进入该文件夹的前提下。

  1. 初始化一个Git仓库并设置用户名和邮箱

    git init
    git config user.name "yourname"
    git config user.email "youremail"
    
  2. 新建一个文件 test.txt

    echo "learn_git 2.3.1" > test.txt
    
  3. 查看仓库状态

    git status
    

    输出:

    位于分支 main
    
     尚无提交
    
     未跟踪的文件:
     (使用 "git add <文件>..." 以包含要提交的内容)
         test.txt
    
     提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)
    
  4. 将文件添加到暂存区

    git add test.txt
    
  5. 再次查看仓库状态

    git status
    

    输出:

    位于分支 main
    
    尚无提交
    
    要提交的变更:
    (使用 "git rm --cached <文件>..." 以取消暂存)
        新文件:   test.txt
    
  6. 提交文件到版本库

    git commit -m "add test.txt"
    
  7. 再次查看仓库状态

    git status
    

    输出:

    位于分支 main
    无文件要提交,干净的工作区
    

2.3.2 修改文件并提交
#

  1. 修改文件 test.txt

    echo "learn_git 2.3.2" > test.txt
    
  2. 查看仓库状态

    git status
    

    输出:

    位于分支 main
    尚未暂存以备提交的变更:
    (使用 "git add <文件>..." 更新要提交的内容)
    (使用 "git restore <文件>..." 丢弃工作区的改动)
        修改:     test.txt
    
    修改尚未加入提交(使用 "git add" 和/或 "git commit -a"
  3. 这次我们使用 git commit -am命令,它会自动将所有已修改的文件添加到暂存区并提交

    git commit -a -m "modify test.txt to 2.3.2"
    
  4. 再次查看仓库状态

     git status
    

    输出:

    位于分支 main
    无文件要提交,干净的工作区
    

2.3.3 查看提交历史
#

  1. 查看提交历史

    git log
    

    输出:

    commit SHA-1 40-character commit hash (HEAD -> main)
    Author: yourname <youremail>
    Date:   Day of week Month Date hh:mm:ss Year +UTC
    
        modify test.txt to 2.3.2
    
    commit SHA-1 40-character commit hash
    Author: yourname <youremail>
    Date:   Day of week Month Date hh:mm:ss Year +UTC
    
        add test.txt
    
  2. 查看提交历史整洁版

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 (HEAD -> main) [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    

2.3.4 撤销修改(切换版本)
#

  1. 查看当前文件内容

    cat test.txt
    

    输出:

    learn_git 2.3.2
    
  2. 撤销修改,切换到指定版本

    git checkout 7_hash_1(为2.3.3查看提交历史整洁版输出结果的哈希值)
    

    输出:

    注意:正在切换到 '7_hash_1'
    您正处于分离头指针状态。您可以查看、做试验性的修改及提交,并且您可以在切换
    回一个分支时,丢弃在此状态下所做的提交而不对分支造成影响。
    
    如果您想要通过创建分支来保留在此状态下所做的提交,您可以通过在 switch 命令
    中添加参数 -c 来实现(现在或稍后)。例如:
    
      git switch -c <新分支名>
    
    或者撤销此操作:
    
      git switch -
    
    通过将配置变量 advice.detachedHead 设置为 false 来关闭此建议
    
    HEAD 目前位于 7_hash_1 add test.txt
    
  3. 查看当前文件内容

    cat test.txt
    

    输出:

    learn_git 2.3.1
    
  4. 切换回最新版本

    git checkout main
    

    输出:

    之前的 HEAD 位置是 7_hash_1 add test.txt
    切换到分支 'main'
    
  5. 查看当前文件内容

    cat test.txt
    

    输出:

    learn_git 2.3.2
    

2.3.5 撤销修改-本地已保存状态
#

  1. 修改文件 test.txt

    echo "learn_git 2.3.5" > test.txt
    
  2. 查看当前文件内容

    cat test.txt
    

    输出:

    learn_git 2.3.5
    
  3. 恢复本地修改

    git checkout test.txt
    
  4. 查看当前文件内容

    cat test.txt
    

    输出:

    learn_git 2.3.2
    

2.3.6 撤销修改-已暂存状态下
#

  1. 修改文件 test.txt

    echo "learn_git 2.3.6" > test.txt
    
  2. 添加到暂存区

    git add test.txt
    
  3. 查看仓库状态

    git status
    

    输出:

    位于分支 main
    要提交的变更:
      (使用 "git restore --staged <文件>..." 以取消暂存)
    	修改:     test.txt
    
  4. 撤销暂存文件

    git reset HEAD test.txt
    
  5. 查看仓库状态

    git status
    

    输出:

    位于分支 main
    尚未暂存以备提交的变更:
      (使用 "git add <文件>..." 更新要提交的内容)
      (使用 "git restore <文件>..." 丢弃工作区的改动)
    	修改:     test.txt
    
    修改尚未加入提交(使用 "git add" 和/或 "git commit -a"
  6. 恢复本地修改

    git checkout test.txt
    
  7. 查看仓库状态

    git status
    

    输出:

    位于分支 main
    无文件要提交,干净的工作区
    
  8. 查看当前文件内容

     cat test.txt
    

    输出:

    learn_git 2.3.2
    

2.3.7 辨析 git checkoutgit reset
#

特性git checkoutgit reset
主要用途切换分支或恢复工作树文件重置当前HEAD到指定状态
影响范围主要影响工作目录可以影响暂存区和/或工作目录
常见用法git checkout <branch>
git checkout <file>
git reset --soft <commit>
git reset --mixed <commit>
git reset --hard <commit>
对提交历史的影响不改变提交历史可以改变提交历史(使用 --hard
文件层面操作可以检出单个文件主要用于提交层面,但也可以重置单个文件
分支操作可以创建和切换分支不能直接切换分支
数据安全性相对安全,不会丢失数据使用 --hard可能会丢失未提交的更改
撤销操作可以撤销工作目录的修改可以撤销提交、暂存的更改

在进行 Git 操作时,请根据您的需要选择正确的命令:

  • 如果您想要撤销某些更改并将历史记录回滚到旧的提交,则应使用 git reset命令。
  • 如果您只是想查看其他提交的状态或切换到不同的分支,则应使用 git checkout命令。

2.3.8 撤销提交
#

  1. 修改文件 test.txt

    echo "learn_git 2.3.8" > test.txt
    
  2. 添加所有修改的文件到暂存区并提交

    git commit -am "modify test.txt to 2.3.8"
    
  3. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 (HEAD -> main) [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    
  4. 撤销最后一次提交,并创建一个新的提交来还原更改。

    git revert HEAD --no-edit
    
  5. 查看当前文件内容

    cat test.txt
    

    输出:

    learn_git 2.3.2
    
  6. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_4 year-month-day | Revert "modify test.txt to 2.3.8" (HEAD -> main) [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    
  7. 恢复上一次撤销提交

    git reset --hard HEAD^
    
  8. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 (HEAD -> main) [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    
  9. 查看当前文件内容

    cat test.txt
    

    输出:

    learn_git 2.3.8
    
  10. 撤销到指定提交

    git revert 7_hash_2 --no-edit
    

    输出:

    自动合并 test.txt
    冲突(内容):合并冲突于 test.txt
    错误:不能还原 6396343... modify test.txt to 2.3.2
    提示: 冲突解决完毕后,用 'git add <路径>''git rm <路径>'
    提示: 命令标记修正后的文件
    提示: Disable this message with "git config advice.mergeConflict false"
    
  11. 这时发现有冲突,我们需要手动解决冲突,编辑文件 test.txt,修改为:

    learn_git 2.3.2
    

    即采用传入的更改

  12. 标记解决并完成revert

    git add test.txt
    git revert --continue
    
  13. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_5 year-month-day | Revert "modify test.txt to 2.3.2" (HEAD -> main) [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    

2.3.9 删除提交
#

  1. 修改文件并提交

    echo "learn_git 2.3.9" > test.txt
    git commit -am "modify test.txt to 2.3.9"
    
  2. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_6 year-month-day | modify test.txt to 2.3.9 (HEAD -> main) [yourname]
    * 7_hash_5 year-month-day | Revert "modify test.txt to 2.3.2" [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    
  3. 删除提交(标记删除 7_hash_3后面所有提交)

    git reset --hard 7_hash_3
    
  4. 查看提交历史

     git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 (HEAD -> main) [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    

2.3.10 修改提交内容
#

  1. 修改文件并提交

    echo "learn_git 2.3.10" > test.txt
    git commit -am "modify test.txt to 2.3.10"
    
  2. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_7 year-month-day | modify test.txt to 2.3.10 (HEAD -> main) [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    
  3. 再次修改文件并覆盖上一次提交

    echo "learn_git 2.3.10_1" > test.txt
    git commit --amend -m "modify test.txt to 2.3.10_1"
    
  4. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 (HEAD -> main) [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    

2.3.11 移动文件
#

  1. 移动文件 test.txttest/test.txt

    mkdir test
    git mv test.txt test/test.txt
    
  2. 查看仓库状态

    git status
    

    输出:

    位于分支 main
    要提交的变更:
      (使用 "git restore --staged <文件>..." 以取消暂存)
        重命名:   test.txt -> test/test.txt
    
    尚未暂存以备提交的变更:
     (使用 "git add <文件>..." 更新要提交的内容)
      (使用 "git restore <文件>..." 丢弃工作区的改动)
        修改:     test/test.txt
    
  3. 提交文件移动

    git commit -m "move test.txt to test/test.txt"
    
  4. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_9 year-month-day | move test.txt to test/test.txt (HEAD -> main) [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    
  5. 恢复到上一次提交

    git reset --hard 7_hash_8
    
  6. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 (HEAD -> main) [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    
  7. 移动文件 test.txttest_1/test.txt

    mkdir test_1
    mv test.txt test_1
    
  8. 查看仓库状态

    git status
    

    输出:

    位于分支 main
    尚未暂存以备提交的变更:
      (使用 "git add/rm <文件>..." 更新要提交的内容)
      (使用 "git restore <文件>..." 丢弃工作区的改动)
    	删除:     test.txt
    
    未跟踪的文件:
      (使用 "git add <文件>..." 以包含要提交的内容)
    	test_1/
    
    修改尚未加入提交(使用 "git add" 和/或 "git commit -a"
  9. 暂存文件

    git add test_1
    
  10. 删除文件

    git rm test.txt
    
  11. 查看仓库状态

    git status
    

    输出:

    位于分支 main
    要提交的变更:
      (使用 "git restore --staged <文件>..." 以取消暂存)
    	重命名:   test.txt -> test_1/test.txt
    
  12. 暂存文件并提交

    git commit -am "move test.txt to test_1/test.txt"
    
  13. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt (HEAD -> main) [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    
  14. 重命名文件 test_1/test.txttest_1/test_1.txt

    git mv test_1/test.txt test_1/test_1.txt
    
  15. 查看仓库状态

    git status
    

    输出:

    位于分支 main
    要提交的变更:
      (使用 "git restore --staged <文件>..." 以取消暂存)
    	重命名:   test_1/test.txt -> test_1/test_1.txt
    
  16. 暂存文件并提交

    git commit -am "rename test_1/test.txt to test_1/test_1.txt"
    
  17. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_11 year-month-day | rename test_1/test.txt to test_1/test_1.txt (HEAD -> main) [yourname]
    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    

2.3.12 忽略文件(.gitignore
#

  1. 创建一个 .gitignore文件,并设置忽略 *.log文件

    echo "*.log" > .gitignore
    
  2. 暂存文件并提交

    git add .gitignore
    git commit -m "add .gitignore"
    
  3. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_12 year-month-day | add .gitignore (HEAD -> main) [yourname]
    * 7_hash_11 year-month-day | rename test_1/test.txt to test_1/test_1.txt [yourname]
    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    
  4. 创建测试文件 test.log

    echo "test.log 2.3.12" > test.log
    
  5. 查看仓库状态

    git status
    

    输出:

    位于分支 main
    无文件要提交,干净的工作区
    

2.3.13 标签操作
#

  1. 把最新提交打上标签

    git tag v1.0
    
  2. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_12 year-month-day | add .gitignore (HEAD -> main, tag: v1.0) [yourname]
    * 7_hash_11 year-month-day | rename test_1/test.txt to test_1/test_1.txt [yourname]
    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    
  3. 删除标签

    git tag -d v1.0
    
  4. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_12 year-month-day | add .gitignore (HEAD -> main) [yourname]
    * 7_hash_11 year-month-day | rename test_1/test.txt to test_1/test_1.txt [yourname]
    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    

3. Git进阶使用
#

3.1 Git对象存储机制
#

Git 使用一种称为对象存储的机制来管理和处理所有文件和目录内容,以及它们之间的关系。

Git 对象存储机制包括以下几个方面:

  • Git 对象:Git 中的所有数据都被视为对象。一个对象可以是一个文件的内容(Blob)、目录结构(Tree)、提交记录(Commit)或标签(Tag)等。
  • SHA-1 哈希:每个 Git 对象都具有与其内容相关联的唯一标识符,该标识符由其内容的 SHA-1 哈希值生成。这使得 Git 可以轻松地检测文件内容的更改。
  • Git 数据库:Git 会将所有对象存储在一个数据库中,该数据库位于 .git/objects目录下。该目录包含一个名为 info的子目录和一个名为 pack的子目录,其中 info子目录包含有关对象的元数据,而 pack子目录包含经过压缩的对象。

3.1.1 Git对象
#

Git 数据库包含了许多不同类型的对象,这些对象相互关联形成一个有向无环图(DAG)。在 Git 中,每个对象都由 SHA-1 哈希值唯一标识,并按照其哈希值存储在 .git 目录下的 objects 目录中。

Git 对象包括四种主要类型:blobtreecommittag

  • Blob:Blob 对象代表一个文件的内容。每个 blob 都由一个唯一的 SHA-1 哈希值标识,并存储在 .git/objects 目录下。Blob 对象是 Git 数据库的基本单位,它们包含文件的原始内容而不包含任何元数据。
  • Tree:Tree 对象代表一个目录或文件夹,在 Git 中被称为“树”。它可以包含多个 blob 或其他 tree 对象,以及相关元数据,如文件名和权限等信息。Tree 对象也由一个 SHA-1 哈希值唯一标识,并存储在 .git/objects 目录下的 objects/trees 子目录中。
  • Commit:Commit 对象代表一个提交记录,包含了提交的作者、提交者、提交时间、提交信息等元数据,以及指向一个 tree 对象的指针。每个 commit 对象都由一个唯一的 SHA-1 哈希值标识,并存储在 .git/objects 目录下的 objects/commits 子目录中。
  • Tag:Tag 对象代表一个标签,用于标记某个特定的 commit 对象。Tag 对象包含了标签的名称、标签的类型、标签的作者、标签的创建时间等元数据,以及指向一个 commit 对象的指针。Tag 对象也由一个唯一的 SHA-1 哈希值标识,并存储在 .git/objects 目录下的 objects/tags 子目录中。

ps:有关 git cat-file会帮助你深入了解Git内部机制,这里不多做介绍,需要了解的可以自行查阅。

3.2 分支管理
#

3.2.1 创建分支
#

在 Git 中,分支是指针,它指向某个提交记录。在 Git 存储库中,默认情况下有一个名为 main的主分支,该分支指向最新的提交记录。

使用分支可以轻松地将代码库分成不同的版本,并在这些版本之间进行切换和合并操作。例如,如果你想尝试新功能或修复错误,可以创建一个新分支,在该分支上进行更改,而不会影响主分支。一旦更改准备好,就可以将其合并回主分支中。这使得协作变得更加容易,因为团队成员可以在自己的分支上开发新功能,而不必担心与其他人的更改冲突。

  1. 创建一个新分支 feature

    git checkout -b feature
    
  2. 查看所有分支

    git branch
    

    输出:

    * feature
    main
    
  3. 查看仓库状态

    git status
    

    输出:

    位于分支 feature
    无文件要提交,干净的工作区
    
  4. 新建文件 feature.txt并提交

    echo "feature branch" > feature.txt
    git add feature.txt
    git commit -m "add feature.txt"
    
  5. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_13 year-month-day | add feature.txt (HEAD -> feature) [yourname]
    * 7_hash_12 year-month-day | add .gitignore (main) [yourname]
    * 7_hash_11 year-month-day | rename test_1/test.txt to test_1/test_1.txt [yourname]
    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    

3.2.2 合并分支
#

在 Git 中,合并分支是一种将两个不同的分支中的代码更改合并到一个分支中的操作。这允许团队成员在不同的分支中开发功能和修复错误,并最终将它们合并到主分支或其他稳定分支中。

  1. 切换到主分支

    git checkout main
    
  2. 合并 feature分支到 main分支

    git merge feature
    
  3. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_13 year-month-day | add feature.txt (HEAD -> main, feature) [yourname]
    * 7_hash_12 year-month-day | add .gitignore [yourname]
    * 7_hash_11 year-month-day | rename test_1/test.txt to test_1/test_1.txt [yourname]
    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    

3.2.3 合并冲突
#

在 Git 中,当尝试将两个不同的分支合并时,可能会出现合并冲突。这通常发生在两个分支上都更改了同一文件的同一部分时。

  1. 修改文件 feature.txt并提交

    echo "changed by main 3.2.3" > feature.txt
    git commit -am "modify feature.txt by main"
    
  2. 切换到 feature分支

    git checkout feature
    
  3. 修改文件 feature.txt并提交

    echo "changed by feature 3.2.3" > feature.txt
    git commit -am "modify feature.txt by feature"
    
  4. 合并 feature分支到 main分支

    git checkout main
    git merge feature
    

    输出:

    自动合并 feature.txt
    冲突(内容):合并冲突于 feature.txt
    自动合并失败,修正冲突然后提交修正的结果。
    
  5. 手动合并冲突

    cat feature.txt
    

    输出:

    <<<<<<< HEAD
    changed by main 3.2.3
    =======
    changed by feature 3.2.3
    >>>>>> feature
    

    由于我之前设置了 vscode作为默认编辑器,所以这里会自动打开 vscode编辑器,手动解决冲突,点击 保留双方更改

    changed by main 3.2.3
    changed by feature 3.2.3
    
  6. 提交合并结果

    git add feature.txt
    git commit -m "merge feature branch and resolve conflict"
    
  7. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_15 year-month-day | merge feature branch and resolve conflict (HEAD -> main) [yourname]
    |\
    | * 7_hash_14 year-month-day | modify feature.txt by feature (feature) [yourname]
    * | 7_hash_13 year-month-day | modify feature.txt by main [yourname]
    |/
    * 7_hash_12 year-month-day | add .gitignore [yourname]
    * 7_hash_11 year-month-day | rename test_1/test.txt to test_1/test_1.txt [yourname]
    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    

3.2.4 变基和合并
#

Rebasing(变基) 和 Merging(合并) 是 Git 中常用的两种集成分支的方法。这两种方法都可以将一个分支的更改合并到另一个分支中,但它们的内部实现不同,因此其使用场景和结果也略有不同。

  • Rebasing Rebasing 是一种将分支更改应用于目标分支的方法,它会将分支的每个提交都转移到目标分支的顶部,并在每个提交之间将目标分支的更改应用于分支更改。这意味着,当你使用 Rebasing 方法时,最终的提交历史记录是一个线性的历史记录,其中所有更改都按照时间顺序排列。

    1. 主要优点:
      • 提交历史记录更加干净和有序。
      • 可以快速解决由于分支变化而导致的代码冲突。
    2. 主要缺点:
      • 可能需要耗费大量时间和精力来处理冲突。
      • 对于多人协作开发而言,可能需要进行协调才能确保不出现问题。
  • Merging Merging 是一种将分支更改合并到目标分支的方法。它会创建一个新的合并提交,该提交包含了目标分支和要合并的分支的全部更改。这意味着,当你使用 Merging 方法时,最终的提交历史记录将包含合并提交以及两个分支的更改历史记录。

    1. 主要优点:
      • 容易理解和使用。
      • 不需要手动处理冲突。
    2. 主要缺点:
      • 提交历史记录可能会变得杂乱无序,难以阅读和理解。
      • 如果分支更改频繁,可能会导致大量的冲突。

综上所述,RebasingMerging 都是有效的集成分支的方法,它们适用于不同的场景。

  • 如果你需要保持提交历史记录的整洁和有序,或者需要快速解决由于分支变化而导致的代码冲突,则可以选择使用 Rebasing 方法。
  • 如果你需要简单地将一个分支的更改合并到另一个分支中,并且不关心最终的提交历史记录,则可以选择使用 Merging 方法。

3.2.5 使用变基合并分支
#

  1. 重置到合并前的最后一个提交

    git reset --hard 7_hash_13
    
  2. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_13 year-month-day | modify feature.txt by main (HEAD -> main) [yourname]
    * 7_hash_12 year-month-day | add .gitignore [yourname]
    * 7_hash_11 year-month-day | rename test_1/test.txt to test_1/test_1.txt [yourname]
    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    
  3. 变基 feature分支到 main分支

    git checkout feature
    git rebase main
    
  4. 手动合并冲突

    cat feature.txt
    

    输出合并后结果:

    changed by main 3.2.3
    changed by feature 3.2.3
    
  5. 提交合并结果

    git add feature.txt
    git commit -m "rebase feature branch and resolve conflict"
    git rebase --continue
    
  6. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_16 year-month-day | rebase feature branch and resolve conflict (HEAD -> feature) [yourname]
    * 7_hash_13 year-month-day | modify feature.txt by main [yourname]
    * 7_hash_12 year-month-day | add .gitignore [yourname]
    * 7_hash_11 year-month-day | rename test_1/test.txt to test_1/test_1.txt [yourname]
    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    
  7. 切换到 main分支

    git checkout main
    
  8. 合并 feature分支到 main分支

    git merge feature
    
  9. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_17 year-month-day | rebase feature branch and resolve conflict (HEAD -> main, feature) [yourname]
    * 7_hash_13 year-month-day | modify feature.txt by main [yourname]
    * 7_hash_12 year-month-day | add .gitignore [yourname]
    * 7_hash_11 year-month-day | rename test_1/test.txt to test_1/test_1.txt [yourname]
    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    

3.3 多存储库
#

到目前为止,我们一直在使用单个 git 存储库。然而,git 擅长处理多个存储库。这些额外的存储库可以本地存储,也可以通过网络连接访问。

本节我们将创建一个名为 learn_git_cloned的新存储库。展示如何从一个存储库移动更改到另一个存储库,并且当两个存储库之间发生冲突时如何处理。

目前,我们将使用本地存储库(即存储在本地硬盘上的存储库)进行工作,但是,在本节中学到的大多数内容都适用于多个存储库,无论它们是在本地还是通过网络远程存储。

3.3.1 克隆存储库
#

  1. learn_git同级目录下克隆 learn_git存储库

    git clone learn_git learn_git_cloned
    
  2. 进入 learn_git_cloned目录,并查看提交历史

    cd learn_git_cloned
    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_17 year-month-day | rebase feature branch and resolve conflict (HEAD -> main, feature) [yourname]
    * 7_hash_13 year-month-day | modify feature.txt by main [yourname]
    * 7_hash_12 year-month-day | add .gitignore [yourname]
    * 7_hash_11 year-month-day | rename test_1/test.txt to test_1/test_1.txt [yourname]
    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    
  3. 查看远程存储库

    git remote -v
    

    输出:

    origin  learn_git (fetch)
    origin  learn_git (push)
    
  4. 查看详细信息

    git remote show origin
    

    输出:

    * 远程 origin
      获取地址:learn_git
      推送地址:learn_git
      HEAD 分支:main
      远程分支:
        feature 已跟踪
        main    已跟踪
    'git pull' 配置的本地分支:
        main 与远程 main 合并
    'git push' 配置的本地引用:
        main 推送至 main (最新)
    
  5. 查看分支

    git branch -a
    

    输出:

    * main
      remotes/origin/HEAD -> origin/main
      remotes/origin/feature
      remotes/origin/main
    

3.3.2 从原始存储库拉取更改
#

  1. learn_git存储库中创建文件 test.txt并提交

    cd learn_git
    echo "add test.txt by learn_git 3.3.2" > test.txt
    git add test.txt
    git commit -m "add test.txt by learn_git 3.3.2"
    
  2. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_18 year-month-day | add test.txt by learn_git 3.3.2 (HEAD -> main) [yourname]
    * 7_hash_17 year-month-day | rebase feature branch and resolve conflict (feature) [yourname]
    * 7_hash_13 year-month-day | modify feature.txt by main [yourname]
    * 7_hash_12 year-month-day | add .gitignore [yourname]
    * 7_hash_11 year-month-day | rename test_1/test.txt to test_1/test_1.txt [yourname]
    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    
  3. 进入 learn_git_cloned目录,拉取 learn_git存储库的更改

    cd learn_git_cloned
    git pull origin main
    
  4. learn_git存储库中修改文件 test.txt并提交

    cd learn_git
    echo "modify test.txt by learn_git 3.3.2" > test.txt
    git add test.txt
    git commit -m "modify test.txt by learn_git 3.3.2"
    
  5. 进入 learn_git_cloned目录,获取 learn_git存储库的更改

    cd learn_git_cloned
    git fetch
    
  6. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short --all
    

    输出:

    * 7_hash_19 year-month-day | modify test.txt by learn_git 3.3.2 (origin/main, origin/HEAD) [yourname]
    * 7_hash_18 year-month-day | add test.txt by learn_git 3.3.2 (HEAD -> main) [yourname]
    * 7_hash_17 year-month-day | rebase feature branch and resolve conflict (origin/feature) [yourname]
    * 7_hash_13 year-month-day | modify feature.txt by main [yourname]
    * 7_hash_12 year-month-day | add .gitignore [yourname]
    * 7_hash_11 year-month-day | rename test_1/test.txt to test_1/test_1.txt [yourname]
    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    
  7. 合并已经获取的更改

    git merge origin/main
    
  8. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_19 year-month-day | modify test.txt by learn_git 3.3.2 (HEAD -> main, origin/main, origin/HEAD) [yourname]
    * 7_hash_18 year-month-day | add test.txt by learn_git 3.3.2 [yourname]
    * 7_hash_17 year-month-day | rebase feature branch and resolve conflict (origin/feature) [yourname]
    * 7_hash_13 year-month-day | modify feature.txt by main [yourname]
    * 7_hash_12 year-month-day | add .gitignore [yourname]
    * 7_hash_11 year-month-day | rename test_1/test.txt to test_1/test_1.txt [yourname]
    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    

3.3.3 向原始存储库推送更改
#

  1. 设置用户名和邮箱,并且设置为裸仓库

    git config user.name "yourname"
    git config user.email "youremail"
    git config --bool core.bare true
    
  2. learn_git_cloned存储库中修改文件 test.txt并提交

    echo "modify test.txt by learn_git 3.3.3" > test.txt
    git add cloned.txt
    git commit -m "modify test.txt by learn_git 3.3.3"
    
  3. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_20 year-month-day | modify test.txt by learn_git 3.3.3 (HEAD -> main) [yourname]
    * 7_hash_19 year-month-day | modify test.txt by learn_git 3.3.2 (origin/main, origin/HEAD) [yourname]
    * 7_hash_18 year-month-day | add test.txt by learn_git 3.3.2 [yourname]
    * 7_hash_17 year-month-day | rebase feature branch and resolve conflict (origin/feature) [yourname]
    * 7_hash_13 year-month-day | modify feature.txt by main [yourname]
    * 7_hash_12 year-month-day | add .gitignore [yourname]
    * 7_hash_11 year-month-day | rename test_1/test.txt to test_1/test_1.txt [yourname]
    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    
  4. 设置learn_git存储库接收更改

    cd learn_git
    git config receive.denyCurrentBranch updateInstead
    
  5. 推送更改到 learn_git存储库

    cd learn_git_cloned
    git push origin main
    
  6. 查看提交历史

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    

    输出:

    * 7_hash_20 year-month-day | modify test.txt by learn_git 3.3.3 (HEAD -> main, origin/main, origin/HEAD) [yourname]
    * 7_hash_19 year-month-day | modify test.txt by learn_git 3.3.2 [yourname]
    * 7_hash_18 year-month-day | add test.txt by learn_git 3.3.2 [yourname]
    * 7_hash_17 year-month-day | rebase feature branch and resolve conflict (origin/feature) [yourname]
    * 7_hash_13 year-month-day | modify feature.txt by main [yourname]
    * 7_hash_12 year-month-day | add .gitignore [yourname]
    * 7_hash_11 year-month-day | rename test_1/test.txt to test_1/test_1.txt [yourname]
    * 7_hash_10 year-month-day | move test.txt to test_1/test.txt [yourname]
    * 7_hash_8 year-month-day | modify test.txt to 2.3.10_1 [yourname]
    * 7_hash_3 year-month-day | modify test.txt to 2.3.8 [yourname]
    * 7_hash_2 year-month-day | modify test.txt to 2.3.2 [yourname]
    * 7_hash_1 year-month-day | add test.txt [yourname]
    

88. 补充资料
#

xiadengma
作者
xiadengma
Git掌握与实践 - 系列文章
§ : 本文