成为git砖家(5): 理解 HEAD

文章目录

    • 1. git rev-parse 命令
    • 2. 什么是 HEAD
      • 2.1 创建分支当并未切换, HEAD 不变
      • 2.2 切换分支,HEAD 改变
      • 2.3 再次切换分支, HEAD 再次改变
    • 3. detached HEAD
    • 4. HEAD 表示分支、表示 detached HEAD 有什么区别?
      • 区别
      • 相同点
    • 5. `HEAD~`, `HEAD^`, `HEAD~1`, `HEAD^1`, `HEAD~n`, `HEAD^2` 用法说明
      • 5.1 概念浅析
      • 5.2 加深理解 - 准备可复现的测试工程
        • `generate_commits.sh`:
        • `git_commit_to_binary_tree.py`
        • 生成 .png 图像
      • 5.3 `HEAD~`, `HEAD^`, `HEAD~1`, `HEAD^1`, `HEAD^2` 的理解
      • 5.4 `HEAD~1`, `HEAD~2`, `HEAD~3`, `HEAD~4`, `HEAD~5` 的直观理解
    • 6. `~` 和 `^` 不仅限于 HEAD 使用
    • 7. git push -u origin HEAD 怎么理解?
    • 8. 总结

1. git rev-parse 命令

git rev-parse 命令是一个非常有用的 git 命令, 主要用于解析和转换 git 对象的引用(例如分支名、标签、提交哈希等)为更具体、更底层的哈希值。

假设当前处于 main 分支,那么 HEAD 显然和 main 表达同样的含义,转换为对应的哈希值是一样的:

git rev-parse main
git rev-parse HEAD

在这里插入图片描述
当然,完整的 git hash值有40位,没法让人一下子记住,我们可以只查看段的hash值,默认是7位:

git rev-parse --short main
git rev-parse --short HEAD

在这里插入图片描述

2. 什么是 HEAD

在 Pro Git 这本书中很好的解释了 HEAD 的概念: 指向当前所在的分支。作为验证, 可以通过查看 .git/HEAD 文件内容,或 git rev-parse HEAD 命令来确认。

2.1 创建分支当并未切换, HEAD 不变

git branch testing

此时创建了新分支 testing, 但并且切换到新分支, 仍处于老的分支 master, 此时 HEAD 指向 master:
在这里插入图片描述

2.2 切换分支,HEAD 改变

当执行了分支切换的命令后,HEAD随之改变:

git checkout testing

在这里插入图片描述

2.3 再次切换分支, HEAD 再次改变

当从 testing 分支切换回 master 分支, HEAD 也随之改变:

git checkout master

在这里插入图片描述

3. detached HEAD

有时候切换到某个 commit 时,并未指定分支名字, 这叫做游离状态的 HEAD。

git checkout <hash>

在这里插入图片描述
可以借助 git图形化界面工具如 gitk,查看当前 commit 情况,其中黄色节点 conv1x1 (42e6766) 是 detached HEAD:

gitk --all

在这里插入图片描述
作为验证,使用 git rev-parse HEAD 可以得到对应的哈希值:
在这里插入图片描述

4. HEAD 表示分支、表示 detached HEAD 有什么区别?

区别

区别在于 detached HEAD 情况下, git branch 返回的不是分支名字:

在这里插入图片描述
在这里插入图片描述
此时的 .git/HEAD 文件内容也变为了具体的hash值:
在这里插入图片描述

而如果是常规的 HEAD (处于分支),git branch 命令得到分支名字:
在这里插入图片描述

相同点

不管是出于 detached HEAD 还是常规的分支, git rev-parse HEAD 都是可以使用的, HEAD~1 这样的表达式都是可以使用的。

5. HEAD~, HEAD^, HEAD~1, HEAD^1, HEAD~n, HEAD^2 用法说明

5.1 概念浅析

目前应该找不到比 git在回退版本时HEAD~和HEAD^的作用和区别 这篇还清晰的讲解了,这里简单贴一下个人读后感:

  • HEAD~ 等价于 HEAD~1
  • HEAD^ 等价于 HEAD^1
  • HEAD~1 表示回退一步,退到第一个父节点上
  • HEAD^1 表示回退到前一步的第一个父节点上
  • HEAD^2 表示回退到前一步的第二个父节点上
  • HEAD~n 表示回退到前n步的第一个父节点上

5.2 加深理解 - 准备可复现的测试工程

下面给出可以复现的步骤来进行说明:

  • generate_commits.sh 生成测试仓库, 虽然你执行的时候commit 哈希会变,但是commit结构不变、tag名字不变
  • git_commit_to_binary_tree.py: 扫描给定的git仓库的commit记录,生成 .dot 文件
generate_commits.sh:
mkdir my-git-repo
cd my-git-repo
git init# Initial commit
echo "Initial commit" > file.txt
echo "*.txt merge=union" > .gitattributes # https://stackoverflow.com/questions/71369712/how-to-use-git-merge-driver-union
git add file.txt
git commit -m "Initial commit"
git tag rootgit branch dev1
git branch dev2
git branch dev3
git branch dev4# branch dev1
git checkout dev1
echo "dev1 - 1" > file.txt
git commit -am "update readme at dev1 - 1"
git tag A1echo "dev1 - 2" > file.txt
git commit -am "update readme at dev1 - 2"
git tag B1# branch dev2
git checkout dev2
echo "dev2 - 1" > file.txt
git commit -am "update at dev2 - 1"
git tag A2echo "dev2 - 2" > file.txt
git commit -am "update at dev2 - 2"
git tag B2# merge dev1 and dev2
git switch dev1
git merge dev2 --no-edit
git tag C1echo "dev1 - 3" > file.txt
git commit -am "update at dev1 - 3"
git tag D1# branch dev3
git checkout dev3
echo "dev3 - 1" > file.txt
git commit -am "update readme at dev3 - 1"
git tag A3echo "dev3 - 2" > file.txt
git commit -am "update readme at dev3 - 2"
git tag B3# branch dev4
git checkout dev4
echo "dev4 - 1" > file.txt
git commit -am "update at dev4 - 1"
git tag A4echo "dev4 - 2" > file.txt
git commit -am "update at dev4 - 2"
git tag B4# merge dev3 and dev4
git switch dev3
git merge dev4 --no-edit
git tag C3echo "dev3 - 3" > file.txt
git commit -am "update at dev3 - 3"
git tag D3# merge dev1 and dev3
git switch dev1
git merge dev3 --no-edit
git_commit_to_binary_tree.py
import subprocess
import os
from graphviz import Digraph# Step 1: 获取 Git 提交记录
def get_git_commits(repo_path):os.chdir(repo_path)# 获取提交记录,包括简短的哈希值result = subprocess.run(['git', 'log', '--pretty=format:%h %H %P'], stdout=subprocess.PIPE)commit_lines = result.stdout.decode('utf-8').split('\n')commits = []for line in commit_lines:parts = line.split()commit = {"short_hash": parts[0],"hash": parts[1],"parents": parts[2:]}commits.append(commit)return commits# 获取标签信息
def get_git_tags(repo_path):os.chdir(repo_path)result = subprocess.run(['git', 'tag', '-l', '--format=%(objectname) %(refname:short)'], stdout=subprocess.PIPE)tag_lines = result.stdout.decode('utf-8').split('\n')tags = {}for line in tag_lines:parts = line.split()if len(parts) == 2:tags[parts[0]] = parts[1]return tags# 获取当前HEAD的简短哈希
def get_git_head(repo_path):os.chdir(repo_path)result = subprocess.run(['git', 'rev-parse', '--short', 'HEAD'], stdout=subprocess.PIPE)return result.stdout.decode('utf-8').strip()# Step 2: 生成提交记录的二叉树结构
class Node:def __init__(self, commit_hash):self.commit_hash = commit_hashself.label = ""self.left = Noneself.right = Nonedef build_binary_tree(commits, tags, head_short_hash):nodes = {}for commit in commits:short_hash = commit['short_hash']node = Node(short_hash)if commit['hash'] in tags:node.label = tags[commit['hash']]elif short_hash == head_short_hash:node.label = "HEAD"else:node.label = short_hashnodes[commit['hash']] = nodefor commit in commits:node = nodes[commit['hash']]if len(commit['parents']) > 0:node.left = nodes.get(commit['parents'][0], None)if len(commit['parents']) > 1:node.right = nodes.get(commit['parents'][1], None)return nodes# Step 3: 生成 .dot 文件
def generate_dot_file(root_hash, nodes, dot_filename):dot = Digraph()root = nodes[root_hash]def add_edges(node):if node is not None:dot.node(node.commit_hash, label=node.label)if node.left:dot.edge(node.commit_hash, node.left.commit_hash)add_edges(node.left)if node.right:dot.edge(node.commit_hash, node.right.commit_hash)add_edges(node.right)add_edges(root)dot.save(dot_filename)# 使用示例
repo_path = 'my-git-repo'  # 替换为你的Git仓库路径
dot_filename = 'commit_tree.dot'commits = get_git_commits(repo_path)
tags = get_git_tags(repo_path)
head_short_hash = get_git_head(repo_path)
nodes = build_binary_tree(commits, tags, head_short_hash)
root_hash = commits[0]['hash']  # 假设最近的提交为根节点generate_dot_file(root_hash, nodes, dot_filename)

执行:

python git_commit_to_binary_tree.py

会生成 commit_tree.dot 文件。

生成 .png 图像
dot -Tpng commit_tree.dot -o commit_tree.png

打开 commit_tree.png

在这里插入图片描述

5.3 HEAD~, HEAD^, HEAD~1, HEAD^1, HEAD^2 的理解

在这里插入图片描述
在这里插入图片描述

(base) ➜  my-git-repo git:(dev1) git rev-parse HEAD
3d63abe282aebfa3aff013972d2acf2181bf1bf7
(base) ➜  my-git-repo git:(dev1) git rev-parse --short HEAD
3d63abe
(base) ➜  my-git-repo git:(dev1) git rev-parse --short D1
4bd7d08
(base) ➜  my-git-repo git:(dev1) git rev-parse --short D4
7e27b48
(base) ➜  my-git-repo git:(dev1) git rev-parse --short HEAD~
4bd7d08
(base) ➜  my-git-repo git:(dev1) git rev-parse --short HEAD^
4bd7d08
(base) ➜  my-git-repo git:(dev1) git rev-parse --short HEAD~1
4bd7d08
(base) ➜  my-git-repo git:(dev1) git rev-parse --short HEAD^1
4bd7d08
(base) ➜  my-git-repo git:(dev1) git rev-parse --short HEAD^2
7e27b48

5.4 HEAD~1, HEAD~2, HEAD~3, HEAD~4, HEAD~5 的直观理解

HEAD~n 表示第n级祖先节点中的第一个节点。例如红色的 HEAD~1 表示父节点,黄色的 HEAD~2 表示爷爷节点, 绿色的 HEAD~3 表示第3级父节点,蓝色的 HEAD~4 表示第4级父节点。
在这里插入图片描述

对于 B2 节点,应当用 HEAD~2^2 表示: HEAD~2 表达了从 HEAD 到 D1 再到 C1 的路径, ^2 则表达了从 B1, B2 里选择 B2:

在这里插入图片描述

6. ~^ 不仅限于 HEAD 使用

commit 哈希码也可以使用。
tag 也可以使用。

举例:

  • 3d63abe~1
  • 3d63abe^2
  • D1~2
  • C4~
    在这里插入图片描述

7. git push -u origin HEAD 怎么理解?

在新建分支、本地完成开发后,提交到remote的时候,最简短的写法是:

git push -u origin HEAD

其中 -u 表示设置 upstream branch, origin 是 remote 的名字, HEAD 则表示当前分支的名字。假设当前是 dev 分支,那么这就话就等价于

git push -u origin dev

可以说, HEAD 的写法非常简单、可以避免手贱写错当前分支名字,很好用。

8. 总结

HEAD 表示当前分支的别名。当切换分支, .git/HEAD 就变化了。

查看 .git/HEAD 并不是很直观, 直观的方式是用 git rev-parse HEAD 命令, 以及 git rev-parse main 这样的写法。进一步的, 使用 git rev-parse --short HEAD 查看短哈希更佳直观。

HEAD 之外,还可以使用 HEAD~, HEAD^ 的形式, 以及 HEAD~n 的形式。 HEAD^2 表示上一层节点中的第二个节点, 而 HEAD~2 则表示“爷爷节点”。

通过使用 graphviz 和 python,解析了 git 仓库的历史提交记录, 并结合 tag, 直观的理解了 HEAD~2^2 这样的写法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/51520.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

现在有什么赛道可以干到退休?

最近&#xff0c;一则“90后无论男女都得65岁以后退休”的消息在多个网络平台流传&#xff0c;也不知道是真是假&#xff0c;好巧不巧今天刷热点的时候又看到一条这样的热点&#xff1a;现在有什么赛道可以干到退休&#xff1f; 点进去看了几条热评&#xff0c;第一条热评说的…

邮箱收不到验证码邮件,如何调整邮箱设置?

邮箱收不到验证码邮件的原因&#xff1f;邮箱被拦截的解决策略&#xff1f; 有时用户会遇到邮箱收不到验证码邮件的问题&#xff0c;这不仅影响用户体验&#xff0c;还可能带来安全隐患。AokSend将探讨为什么会出现邮箱收不到验证码邮件的问题&#xff0c;并提供调整邮箱设置的…

哪家培训机构PMP考试通过率高,PMP考试有原题吗?

PMP的官方通过率数据并未公布&#xff0c;培训机构公布的通过率仅供参考。通常情况下&#xff0c;培训机构宣传的通过率不会低于90%&#xff0c;但这并不意味着他们有内部的原题。PMI官方的题库是不公开的&#xff0c;因此机构也无法获取到原题。 然而&#xff0c;由于机构能够…

使用 WebSocket 实现实时聊天

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

PHP:连接钉钉接口-钉钉回调事件,本地测试数据

前置数据参考 数据说明:参见官方文档回调事件消息体加解密 - 钉钉开放平台 (dingtalk.com) URL后面带的参数: signature=5a65ceeef9aab2d149439f82dc191dd6c5cbe2c0&timestamp=1445827045067&nonce=nEXhMP4r Post参数: { "encrypt":"1a3NB…

猫咪食欲不振?快来看看开胃主食罐!

夏天到了&#xff0c;天气太热了&#xff0c;不仅我没什么胃口&#xff0c;家里的猫主子也食欲大降&#xff0c;真是把我愁坏了。 我家之前喂德罐为主&#xff0c;小李子、交响乐金罐都囤了不少。但德罐都是巨无霸包装&#xff0c;200g和400g规格的大罐头&#xff0c;开了一餐…

OCCT使用指南:Foundation Classes

1、介绍 本手册解释了如何使用Open CASCADE Technology (OCCT) Foundation Classes。它提供了关于基础类的基础文档。有关基础类及其应用的高级信息&#xff0c;请参阅我们的电子学习和培训产品。 基础类提供各种通用服务&#xff0c;如自动动态内存管理&#xff08;通过句柄操…

python import:跨文件夹导入模块

python 中 import 语句可以往当前 py 文件中导入外部模块&#xff0c;从而使用外部模块中的函数与类。 如果想要导入 python 内置模块和 pip install 安装的模块&#xff0c;可以直接使用 “import …” 的形式导入。 如果想要导入自己本地定义的模块&#xff08;即跨文件夹导入…

设置浏览器ie兼容模式

点击设置 设置IE模式

Animation Clips

动画剪辑是Unity用来表示对象运动姿态的基础资源&#xff0c;你可以从模型文件中导入动画剪辑&#xff0c;也可以在Unity内部创建简单的动画剪辑。 Import Animation From Model 在导入时只需要确保模型的Animation选项卡中的Import Animation是被激活的&#xff0c;对于没有动…

华为诺亚发布无限上下文大模型,超越SoTA 4.3%

你的大语言模型是不是也患上了"长文健忘症"&#xff1f;当使用大模型遇到长上下文时总是会出现词不达意&#xff1f;别担心&#xff0c;LLM界的"记忆大师"来啦&#xff01;华为诺亚方舟实验室最新推出的EM-LLM模型&#xff0c;就像是给大模型装上了"超…

典型二进制翻译系统---用户级翻译

目录 QEMU MDT HQEMU 补充 Valgrind 包括 CrossBit 补充 MTCrossBit Strata-ARM Box86 HQEMU SymQEMU QEMU 静态二进制翻译器 是一个开源的多平台的虚拟器&#xff0c;能够在软件中模拟一台机器 KVM&#xff08;Kernel-based Virtual Machine&#xff09;是 Linu…

内螺纹销主要应用优势及应用领域

内螺纹销是一种常见的机械连接件&#xff0c;它通常用于连接两个或多个零件&#xff0c;并通过其内螺纹结构来实现紧固和定位。这种零件的设计简单却非常实用&#xff0c;在各种工业领域中都能找到它的身影。下面我们就来了解一下内螺纹销的主要应用优势以及它们的应用领域。 应…

华测无人船为水库库容精准测量提供解决方案

水库作为融合防洪、供水、发电及生态维护等多重功能于一体的基础设施,其库容的精准测量对于水资源管理及防洪调度的科学决策具有举足轻重的地位。然而,随着水库使用年限的增加,泥沙淤积现象的加剧以及地理信息更新的滞后,库容复核已成为确保水库安全高效运行不可或缺的一环。 当…

自写ApiTools工具,功能参考Postman和ApiPost

近日在使用ApiPost的时候&#xff0c;发现新版本8和7不兼容&#xff0c;也就是说8不支持离线操作&#xff0c;而7可以。 我想说&#xff0c;我就是因为不想登录使用才从Postman换到ApiPost的。 众所周知&#xff0c;postman时国外软件&#xff0c;登录经常性抽风&#xff0c;…

Redis:管道

1. 面试题 如何优化频繁命令往返造成的性能瓶颈&#xff1f; 问题由来 edis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。一个请求会遵循以下步骤&#xff1a; 1 客户端向服务端发送命令分四步(发送命令→命令排队→命令执行→返回结果)&#xff0c;并监听Socket…

You have an error in your SQL syntax; check the manual that corresponds to your

You have an error in your SQL syntax; check the manual that corresponds to your 目录 You have an error in your SQL syntax; check the manual that corresponds to your 【常见模块错误】 【解决方案】 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班…

python+vue3+onlyoffice在线文档系统实战20240725笔记,首页开发

解决遗留问题 内容区域的高度没有生效&#xff0c;会随着菜单的高度自动变化。 解决方案&#xff1a;给侧边加上一个最小高度。 首页设计 另一种设计&#xff1a; 进来以后&#xff0c;是所有的文件夹和最近的文件。 有一张表格&#xff0c;类似于Windows目录详情&…

每日一题,力扣leetcode Hot100之560.和为K的子数组

解法一&#xff1a; 暴力解法循环直接判断从每个index开始的切片是否满足 class Solution:def subarraySum(self, nums: List[int], k: int) -> int:# 要求的连续子数组count 0n len(nums)for i in range(n):for j in range(i, n):if sum(nums[i:j1]) k:count 1return…

科研绘图系列:R语言山脊图(Ridgeline Chart)

介绍 山脊图(Ridge Chart)是一种用于展示数据分布和比较不同类别或组之间差异的数据可视化技术。它通常用于展示多个维度或变量之间的关系,以及它们在不同组中的分布情况。山脊图的特点: 多变量展示:山脊图可以同时展示多个变量的分布情况,允许用户比较不同变量之间的关…