<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[流年如歌]]></title><description><![CDATA[那是我死心塌地的倔强]]></description><link>https://waitforu.tech</link><image><url>https://waitforu.tech/innei.svg</url><title>流年如歌</title><link>https://waitforu.tech</link></image><generator>Shiro (https://github.com/Innei/Shiro)</generator><lastBuildDate>Tue, 21 Apr 2026 13:00:53 GMT</lastBuildDate><atom:link href="https://waitforu.tech/feed" rel="self" type="application/rss+xml"/><pubDate>Tue, 21 Apr 2026 13:00:53 GMT</pubDate><language><![CDATA[zh-CN]]></language><item><title><![CDATA[KUKA机器人车型调用分析程序开发]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://waitforu.tech/posts/develope/krlParserdocsdevInfo">https://waitforu.tech/posts/develope/krlParserdocsdevInfo</a></blockquote><div><h1 id="kuka">KUKA机器人车型调用分析程序开发</h1><h2 id="">摘要</h2><p>在工业机器人生产现场，车型切换、轨迹验证、工艺问题排查和项目交接都离不开对机器人程序调用关系的理解。传统做法主要依赖调试人员或维护人员逐个阅读 KUKA 机器人备份中的程序文件，这种方式虽然在小规模项目中可行，但在车型数量持续增加、调用层级不断加深的情况下，已经难以兼顾效率、准确性和知识沉淀。基于这一现实需求，本文在前一篇 KRL 文法分析报告的理论基础上，围绕“车型调用关系解析”这一明确目标，设计并实现了一套本地 Web 形态的 KUKA 机器人备份分析程序。</p><p>本文重点介绍该程序的工程实现思路，而不是重复展开编译原理推导。系统以 KUKA 机器人备份 <code>zip</code> 文件为输入，首先完成文件读取、路径筛选和元信息提取；随后将 <code>.src</code> 与 <code>.dat</code> 文件按照模块名进行耦合，形成统一的模块单元；在此基础上，借助 ANTLR4 生成的词法分析器和语法分析器完成程序结构识别，并围绕车型调用链条抽取 <code>Cell 程序 -&gt; P 程序 -&gt; 车型代码 -&gt; 车型程序 -&gt; 轨迹程序</code> 的层级关系；最后通过 Config 配置过滤基础模块和业务噪音，将结果以 JSON 图结构返回给前端，并进一步导出为适合存档和转发的 Excel 表格。</p><p>为兼顾可用性与工程可维护性，系统被划分为 <code>krl-core</code> 与 <code>krl-web</code> 两个主要模块。前者负责语言解析、文件加载、模块组织、调用关系抽取与 Excel 导出，后者负责配置管理、文件上传、同步分析接口以及前端静态页面交互。前端界面基于 Cytoscape 对调用关系进行可视化展示，并提供线体信息视图与车型信息视图；导出模块则基于 Apache POI 将图结构转换为“树形结构区 + 调用矩阵区”的 Excel 表达形式。本文将从输入、解析、过滤、展示与导出的完整链路出发，说明该程序如何将 KRL 语法分析能力落地为一套面向实际产线问题的本地 Web 工具。</p><p><strong>关键词：</strong> KUKA；KRL；调用关系；本地 Web 软件；ANTLR4；Cytoscape；Apache POI</p><blockquote><p>说明：本提纲仅介绍当前项目作为本地 Web 软件时的工程设计与实现，不涉及云端部署、服务治理或 server 模式。本报告包含kuka备份文件结构概述及如何从中获取相应信息、针对&quot;车型调用关系解析&quot;这一需求如何简化语法模型构建核心AST节点、最终的&quot;调用关系&quot;所对应的数据结构的设计思路、SpringBoot与用户交互的方法简述、cytoScape进行可视化的方法简述、Apache.poi导出Excel表的方法简述。</p></blockquote>
<h2 id="1-">1. 背景介绍</h2><ul><li>由于柔性化生产的目标，一条线体往往有多个款式、甚至多个品牌的车型。</li><li>在车型导入更改调用关系时、机器人发生碰撞后轨迹验证时、工艺故障相关问题排查时，在这些种种场景下，都需要对车型调用、轨迹调用有清晰的了解。</li><li>目前厂房里车型数量众多、调用关系交错繁杂，且仍在不断导入新车型，难以仅仅依靠人工来高效、准确、及时的全线梳理轨迹和已导车型。</li><li>开沃厂房几乎全是库卡机器人，且在大小调试后往往都会保留机器人的备份程序。</li></ul><p>在工业机器人项目现场，程序阅读并不是孤立的技术行为，而是与调试、维护、工艺验证和知识交接紧密相关的工程活动。对于一条已经投产的焊装或总装线体而言，每次新车型导入、老车型改造、节拍优化或工艺复核，都会牵涉程序调用关系的重新确认。若无法快速回答“当前车型会走哪些程序”“某个轨迹程序被哪些上层逻辑引用”“某台机器人为何会进入某个程序分支”等问题，现场调试和问题定位就只能退回到低效率的人工阅读状态。</p><p>在实际产线中，这种人工方式存在三个明显问题。第一，程序数量庞大。一个机器人备份中往往同时包含系统文件、模板文件、工艺程序、车型分支程序和轨迹程序，真正与业务相关的内容混杂在大量文件之中。第二，调用关系层级较深。从 <code>Cell</code> 程序到 <code>P</code> 程序，再到车型码、车型程序、轨迹程序，调用链并不是平铺展开的，而是存在明显层级。第三，程序解释高度依赖经验。熟悉项目历史的人员能够较快读懂某段程序，但这种能力难以沉淀，也难以在人员流动后保持稳定。</p><p>与此相对，KUKA 机器人备份文件本身却提供了一个非常有利的基础条件：程序以文本形式保存，并且在标准化调试逐步推进后，命名方式、程序分层和调用模式也逐渐趋于稳定。这使得“通过程序文本自动恢复车型调用关系”成为一个现实可行的方向。也就是说，问题并不是“程序完全无法分析”，而是“现场长期缺乏一套把语言分析能力转化为实用工具的工程化实现”。本文所介绍的程序，正是在这一背景下被开发出来的。</p><p><strong>图片标题：图 1 系统总体流程图（zip 输入到结果输出）</strong></p><p><strong>图片内容描述：</strong> 图中从“机器人备份 zip 文件”开始，依次经过“文件读取与筛选”“模块耦合”“语法解析”“调用关系抽取”“配置过滤”“前端图谱展示”和“Excel 导出”几个阶段，突出程序是一个从备份输入到双结果输出的完整链路。</p><h2 id="2-">2. 程序目标</h2><ul><li>结合目前生产线的特点，确定了所开发程序的目标是自动解析机器人备份文件并输出结构化的车型调用关系。</li><li>说明该系统对调试效率、维护效率、工艺分析和项目交接的价值。</li><li>交代程序输出既包括网页版可交互的图形化结果，也包括可下载与分享的 Excel 表格。</li></ul><h3 id="21-">2.1 目标定义</h3><p>本程序的直接目标，是将 KUKA 机器人备份中的程序文件自动转化为结构化的车型调用关系结果。这里的“结构化”有两层含义：一是后端能够以统一的数据对象描述不同层级的调用节点及其关系，二是前端和导出模块能够稳定消费这些结构化数据，分别生成可交互的图谱结果和可保存的 Excel 结果。</p><p>与第一篇报告中所讨论的“系统性分析 KRL 程序”相比，本文对应的工程实现并没有试图一次性覆盖所有程序语义，而是围绕“调用关系”这一业务目标进行聚焦。也就是说，本系统的首要任务不是解释每一条赋值语句、运动指令或信号联动的完整语义，而是识别哪些程序之间存在稳定的调用与分派关系，并把这些关系组织成能够被现场人员直接使用的结果。</p><h3 id="22-">2.2 业务价值</h3><p>这一目标对现场工作具有直接价值。对调试人员而言，调用关系图可以帮助快速定位某个车型从入口到轨迹的完整链条，减少逐层翻程序的时间。对维护人员而言，当轨迹异常或程序改动引发问题时，可先从调用结构确认受影响范围。对工艺分析人员而言，调用关系表有助于比对不同车型共用或独占的程序路径。对项目交接而言，结构化结果比口头说明更容易沉淀和传播。</p><h3 id="23-">2.3 输出形式</h3><p>为了适应不同使用场景，程序设计了两种输出形式。第一种是本地 Web 页面中的交互式结果，用户可以直接在浏览器中查看线体级和车型级调用关系，并通过节点点击、视图切换等方式理解程序结构。第二种是 Excel 文件，用于存档、转发和跨平台查看。前者强调交互性，后者强调持久化和可分享性，两者共同构成系统的最终输出。</p><p>表 1 对系统目标与输出形式做了概括。</p><table><thead><tr><th> 输出目标 </th><th> 形式 </th><th> 主要用途 </th></tr></thead><tbody><tr><td> 机器人与车型调用关系 </td><td> Web 图谱 </td><td> 现场快速查看、交互式理解调用链 </td></tr><tr><td> 程序层级结构 </td><td> Web 列表与信息面板 </td><td> 查看节点详情、辅助核对元信息 </td></tr><tr><td> 调用关系归档结果 </td><td> Excel 表格 </td><td> 存档、转发、跨平台阅读 </td></tr><tr><td> 配置化过滤结果 </td><td> 结构化 JSON </td><td> 供前端渲染和导出模块复用 </td></tr></tbody></table><h2 id="3-">3. 架构设计及技术选型</h2><ul><li>给出系统从“文件输入”到“调用关系输出”的总体流程。</li><li>说明系统采用本地 Web 形态的原因，即兼顾桌面软件的易用性与浏览器界面的可视化能力。定性项目是一款通过Antlr4进行词法分析和语法分析，设计相应数据结构进行语义分析和相关AST的构建，依靠SpringBoot进行交互，借助cytoScope和Apache.poi进行图形渲染和Excel表格保存。</li><li><p>说明选择 Spring Boot、ANTLR4、Cytoscape、Apache POI 的原因，分析各技术组件在系统中的角色与协作方式，论证这些技术选型在开发效率、稳定性和扩展能力上的合理性。</p></li><li><p>介绍相关数据结构的设计、后端解析核心、Web 交互、导出模块等职责划分。说明 <code>krl-core</code> 负责语言解析与调用关系抽取，<code>krl-web</code> 负责用户交互、结果返回与文件下载。并分析这种架构在可维护性、复用性和扩展性上的优势。</p></li></ul><h3 id="31--web-">3.1 本地 Web 形态的选择</h3><p>本项目最终选择本地 Web 软件而不是传统命令行工具或纯桌面客户端，原因在于它能够兼顾两类优势。一方面，本地启动的形式对现场用户较友好，不需要额外搭建复杂服务环境，只要启动程序并打开浏览器即可使用。另一方面，浏览器界面天然适合呈现图结构、面板交互和表格导出，这使得“调用关系”这一原本较抽象的程序结构能够被直观展示。</p><p>在架构上，系统仍然延续第一篇报告所建立的语言分析基础，但在工程落地时重点围绕调用关系这一主线组织模块，不把所有可能的程序语义都放在同一优先级上。这种处理方式使整体实现既能保持理论上的完整来源，又能把开发力量集中在对业务最有价值的部分。</p><h3 id="32-">3.2 分层结构</h3><p>项目被划分为 <code>krl-core</code> 与 <code>krl-web</code> 两个主要模块。<code>krl-core</code> 承担后端的解析内核，包括文件读取、配置加载、模块耦合、ANTLR4 语法分析、调用关系抽取与 Excel 导出。<code>krl-web</code> 承担用户交互入口，包括配置管理、文件上传、同步分析接口、Excel 下载接口以及前端静态页面。这样的分层不是简单按目录拆分，而是把“语言分析能力”和“交互呈现能力”明确隔离。</p><p>这种结构有三个直接好处。第一，解析能力可以被单独维护，不会因为界面变化而频繁改动核心逻辑。第二，前端与控制器只面对结构化结果，无需理解语言细节。第三，导出、过滤、可视化等外围能力都能围绕统一的数据结构复用同一套分析结果。</p><p><strong>图片标题：图 2 项目分层结构图（<code>krl-core</code> 与 <code>krl-web</code>）</strong></p><p><strong>图片内容描述：</strong> 图中上层为本地 Web 用户界面，下层为 <code>krl-web</code> 接口与配置管理，再下层为 <code>krl-core</code> 的文件读取、模块装配、语法分析、调用关系抽取与 Excel 导出模块，突出“核心解析能力”与“交互层”的分工。</p><h3 id="33-">3.3 技术选型说明</h3><p>从工程实现看，系统主要依赖四类技术：ANTLR4、Spring Boot、Cytoscape 和 Apache POI。</p><p>ANTLR4 的作用是把第一篇报告中的 <code>krl.g4</code> 文法落地为可执行的分析器，使系统具备稳定的词法分析和语法分析能力。Spring Boot 的作用是为本地 Web 形态提供统一入口，便于处理文件上传、配置读取和结果返回。Cytoscape 的作用是把调用关系渲染为交互式拓扑图，帮助用户从结构上理解程序。Apache POI 的作用是将结果导出为标准 Excel 文件，满足结果持久化和分享需求。</p><p>表 2 总结了主要技术组件及其职责。</p><table><thead><tr><th> 技术组件 </th><th> 主要职责 </th><th> 选择原因 </th></tr></thead><tbody><tr><td> ANTLR4 </td><td> 生成 KRL 词法分析器与语法分析器 </td><td> 文法驱动、结构清晰、便于维护与扩展 </td></tr><tr><td> Spring Boot </td><td> 提供本地 Web 接口与静态资源承载 </td><td> 启动简单、接口清晰、适合桌面本地服务 </td></tr><tr><td> Cytoscape </td><td> 渲染调用关系图谱 </td><td> 图结构展示成熟，交互能力强 </td></tr><tr><td> Apache POI </td><td> 导出 Excel 结果 </td><td> 兼容常用办公场景，支持样式和合并单元格 </td></tr><tr><td> Jackson / SnakeYAML </td><td> 配置解析与对象映射 </td><td> 适合 <code>Config.yml</code> 规则化管理 </td></tr></tbody></table><h2 id="4-">4. 机器人备份文件读取流程设计</h2><ul><li>说明输入对象为 KUKA 机器人备份 <code>zip</code> 文件。</li><li>介绍解压、遍历、筛选关键文件的流程，以及如何从备份中提取用于分析的源文件和配置信息。</li><li>说明读取阶段如何识别机器人信息、程序文件和辅助资源。</li></ul><h3 id="41-">4.1 输入对象的特点</h3><p>系统的输入对象是 KUKA 机器人备份 <code>zip</code> 文件。与普通压缩包不同，这类备份并不是为人类直接阅读准备的文档集合，而是控制器文件系统在某一时间点的镜像。程序分析要想建立在真实项目数据之上，就必须先解决“如何以尽可能低损耗的方式读取压缩包内容”的问题。</p><h3 id="42-">4.2 文件读取主链</h3><p>这一阶段的核心类是 <code>KrlZipLoader</code>。它并不只是简单地解压所有文件，而是通过 NIO 文件系统接口把 <code>zip</code> 当作一个可遍历的虚拟文件系统来处理。这样设计的好处是，程序可以保留备份中的原始路径结构，而不是在解压后再依赖额外规则恢复层级关系。对于 KRL 分析来说，文件路径本身就包含模块组织信息，因此保留路径比单纯拿到文件内容更重要。</p><p>在遍历过程中，系统会按配置规则筛选真正需要的文件。被保留下来的文件会进一步读取文本内容、创建时间、修改时间、文件大小、文件后缀和所属目录等信息，并封装为 <code>KrlFile</code> 对象。这样做的目的，是在分析开始之前就把“文件内容”和“文件上下文”统一保存下来。</p><h3 id="43-krlfile-">4.3 <code>KrlFile</code> 的设计思路</h3><p><code>KrlFile</code> 是读取阶段最基础的数据结构之一。它保存的并不只是程序文本，还包括原始路径、文件名、所属目录、创建时间、修改时间、文件大小、扩展名以及文件类型。之所以要保留这些字段，是因为后续分析并不是只依赖文本本身。例如，是否属于系统目录、文件名是否与模块名一致、文件时间是否可用于人工辅助判断，都与这些信息有关。</p><h3 id="44-">4.4 读取阶段的输出</h3><p>读取阶段的直接输出并不是调用关系，而是一组可供后续装配的文件对象集合。此时系统已经知道“有哪些候选文件值得分析”，但还不知道它们在语言层和业务层中的角色。也正因为如此，读取阶段被单独设计为一层独立能力，而不是直接把压缩包内容塞给解析器。</p><h2 id="5-dat--src-">5. <code>.dat</code> 与 <code>.src</code> 模块耦合策略</h2><ul><li>说明 KRL 程序在工程上通常由数据文件和源码文件组成，单独分析任一文件都不完整。</li><li>介绍系统如何按照模块名、路径及文件类型将 <code>.dat</code>、<code>.src</code> 耦合成统一模块。</li><li>说明这种模块化组织的必要性和重要性以及如何为后续语法分析和调用关系提取提供基础数据单元。</li></ul><h3 id="51-">5.1 从文件到模块的必要性</h3><p>KRL 的文件组织方式决定了“文件”并不是最合适的分析粒度。通常情况下，一个模块由同名的 <code>.src</code> 与 <code>.dat</code> 文件共同组成：前者承载程序主体，后者承载变量、结构体、枚举或模块数据。若只从文件层面处理，后续分析很容易出现信息割裂，例如过程定义出现在 <code>.src</code> 中，而相关声明或数据又出现在 <code>.dat</code> 中。</p><h3 id="52-krlmodule-">5.2 <code>KrlModule</code> 与仓库结构</h3><p>为了解决这一问题，系统在 <code>KrlFile</code> 之上引入了 <code>KrlModule</code>。<code>KrlModule</code> 以模块名为中心，同时持有对应的 <code>.src</code> 文件和 <code>.dat</code> 文件，并把它们看作同一分析单元。与之配套的是 <code>ModuleRepository</code>。该类一方面维护“模块名 -&gt; 模块”的主索引，另一方面维护“可调用程序名 -&gt; 所在模块”的反向索引，从而为后续根据程序名回溯所属模块提供支持。</p><p>这种设计的价值在于，它把“压缩包里分散的文件集合”整理成了“可面向语言结构分析的模块集合”。这样，后续的解析器不需要再关心文件是从哪里来的，而只需要面对一个相对稳定的模块输入。</p><h3 id="53-">5.3 模块耦合的工程约束</h3><p>模块耦合并不是任意拼接，而是建立在工程约束之上的。系统默认模块名与文件名一致，且一个模块最多只有一个 <code>.src</code> 和一个 <code>.dat</code>。这类约束与现场实际项目中的命名规范相匹配，也让仓库装配过程更容易发现异常，例如同名文件重复、类型冲突或模块不完整等。</p><h3 id="54-">5.4 为什么这一步不能省略</h3><p>如果直接对所有文件逐个做语法分析，程序当然也能得到某些局部结果，但这些结果很难形成稳定的业务语义。原因在于，KRL 的很多结构只有放到“模块”这一层级上才能被正确理解。模块耦合实际上起到了承上启下的作用：向上承接文件读取结果，向下为语法分析和调用关系识别提供统一输入。</p><h2 id="6-">6. 调用关系解析流程设计</h2><ul><li>介绍从模块中提取 Cell 程序、P 程序、车型代码、车型程序与轨迹程序的过程。</li><li>说明调用关系图的数据结构设计，包括节点类型、节点属性和边关系。</li><li>解释如何从解析结果映射为前端可消费的 JSON 数据。</li></ul><h3 id="61-">6.1 从文法分析到业务结构识别</h3><p>这一阶段是全系统的核心。第一篇报告已经说明了如何通过 <code>krl.g4</code> 识别 KRL 的语言结构；在工程实现中，这一能力首先由 <code>ModuleParser</code> 承接。该类会使用 ANTLR4 生成的 <code>krlLexer</code> 与 <code>krlParser</code> 对 <code>KrlModule</code> 进行解析，并借助访问器把解析树转换为更适合业务处理的结构化结果。</p><p>但本系统并不在这一层停留。仅仅知道某个模块中存在哪些过程、表达式或语句，还不足以直接满足现场用户的需求。真正有价值的是把这些语言结构进一步映射到业务中的调用层次。因此，后续由 <code>CarCallReferenceAnalyze</code> 在模块集合上继续执行业务规则判断，最终恢复出车型调用链条。</p><h3 id="62-">6.2 调用层次的恢复路径</h3><p>在当前项目中，核心恢复路径可以概括为：先从 <code>cell.src</code> 一类入口程序中识别 <code>P</code> 程序的调用；再从 <code>P</code> 程序中识别车型代码与车型程序的分派；再从车型程序中识别实际的轨迹程序调用。这个过程并不只是“看谁调用了谁”，而是结合 KRL 代码中常见的 <code>SWITCH/CASE</code>、程序号、车型码和业务命名习惯，把原始程序结构映射为更贴近现场理解方式的层级关系。</p><h3 id="63-">6.3 调用图数据结构</h3><p>为了承接这一结果，系统设计了统一的调用图节点结构 <code>CallNode</code>。每个节点至少包含以下信息：</p><ul><li>节点唯一标识 <code>id</code></li><li>展示值 <code>value</code></li><li>节点类型 <code>nodeType</code></li><li>相关文本信息 <code>relevantInfo</code></li><li>子节点集合 <code>children</code></li><li>辅助属性集合 <code>propertyMap</code></li></ul><p>在此基础上，<code>NodeType</code> 进一步标识节点角色，例如 <code>CEll</code>、<code>P_PROGRAM</code>、<code>VIRTUAL</code>、<code>CAR_CODE</code>、<code>CAR_PROGRAM</code>、<code>ROUTE_PROCESS</code>。这种设计有两个明显好处。第一，前端不需要理解后端所有解析细节，只要根据统一节点结构渲染即可。第二，Excel 导出模块也可以复用同一份节点数据，不必重新做一套结构转换。</p><h3 id="64-robotinfo-">6.4 <code>RobotInfo</code> 的角色</h3><p>在调用图之外，最终结果还需要保留机器人自身的信息。<code>RobotInfo</code> 因此被设计为结果对象的根层封装，它既包含 <code>robotName</code>、<code>archiveName</code>、<code>archiveDate</code>、<code>version</code>、<code>techPackList</code> 等元信息，也包含 <code>callGraphRoot</code> 这一调用图根节点。这样设计之后，一次分析返回的结果就不只是“若干节点”，而是一组以机器人为单位组织好的完整结果对象。</p><h3 id="65--json-">6.5 面向前端 JSON 的映射</h3><p>由于 <code>RobotInfo</code> 与 <code>CallNode</code> 本身就是树形结构友好的对象，后端在同步分析完成后几乎可以直接将其序列化为 JSON 返回给前端。也就是说，前端接收到的并不是一份重新包装过的展示专用数据，而是与后端核心分析结果高度一致的结构化对象。这种做法减少了数据二次转换的复杂度，也降低了前后端结构不一致的风险。</p><p>表 3 总结了几类关键数据结构及其作用。</p><table><thead><tr><th> 数据结构 </th><th> 所在模块 </th><th> 主要作用 </th></tr></thead><tbody><tr><td> <code>KrlFile</code> </td><td> <code>krl-core</code> </td><td> 表示读取后的单个程序文件及其元信息 </td></tr><tr><td> <code>KrlModule</code> </td><td> <code>krl-core</code> </td><td> 将同名 <code>.src</code> 与 <code>.dat</code> 组合为统一模块 </td></tr><tr><td> <code>CallNode</code> </td><td> <code>krl-core</code> </td><td> 表示调用图中的统一节点结构 </td></tr><tr><td> <code>NodeType</code> </td><td> <code>krl-core</code> </td><td> 标识节点在业务调用链中的角色 </td></tr><tr><td> <code>RobotInfo</code> </td><td> <code>krl-core</code> </td><td> 封装机器人元信息与调用图根节点 </td></tr><tr><td> <code>Config</code> </td><td> <code>krl-core</code> </td><td> 承载过滤规则与加载范围配置 </td></tr></tbody></table><h2 id="7-config-">7. Config 配置驱动的噪音过滤机制</h2><ul><li>说明工业机器人程序中存在大量基础模块、系统模块和业务无关调用，若不加筛选会干扰分析结果。</li><li>介绍 <code>Config.yml</code> 在过滤前缀、后缀、目标模块、忽略规则等方面的作用。介绍他的运行方式: resource代码中有最初内容、config.yml文件进行持久化配置，网页中也可临时修改用于当前分析。</li><li>说明配置驱动方式如何兼顾通用性与项目定制化需求。</li></ul><h3 id="71-">7.1 为什么必须做过滤</h3><p>工业机器人备份中的程序并不全部服务于车型调用分析。系统模块、基础模板、公共子程序、工艺无关例程和某些调试辅助文件如果全部纳入结果，会导致调用图被大量噪音淹没，反而降低工具的可用性。因此，语言上“能识别”并不意味着业务上“都应展示”。</p><h3 id="72-configyml-">7.2 <code>Config.yml</code> 的作用</h3><p>为了把语言识别能力转化为业务可用结果，系统引入了 <code>Config.yml</code>。在结构上，配置文件并不是一组零散开关，而是围绕几类核心问题组织的：哪些路径需要读取，哪些模块或前后缀需要忽略，哪些程序属于重点分析范围，哪些规则用于在噪音与有效调用之间做裁剪。</p><p>在实现上，<code>YamlConfigLoad</code> 负责把 YAML 文本解析为 <code>Config</code> 对象，而 <code>IgnoreRuleByStr</code> 则负责将配置中的前缀、后缀与目标匹配规则真正应用到文件和程序筛选过程中。这种方式让过滤逻辑从代码中解耦出来，避免不同项目只能通过改源码来适配现场差异。</p><h3 id="73-">7.3 三层配置来源</h3><p>在本地 Web 软件中，配置一共存在三层来源。第一层是 <code>krl-core</code> 资源目录中的默认配置，作为程序初始模板。第二层是磁盘上的 <code>Config.yml</code>，由 <code>ConfigStorageService</code> 在程序启动时初始化并持续保留，适合存放长期有效的规则。第三层是页面中的临时配置文本，用户可以通过 <code>Config</code> 按钮打开编辑器，仅对当前这次分析进行覆盖。这种设计既保证了开箱即用，也允许按现场实际情况灵活调整。</p><h3 id="74-">7.4 配置驱动的价值</h3><p>从工程角度看，配置驱动带来的最大好处不是“参数更多”，而是“适配成本更低”。调用关系分析的核心程序无需频繁改动，只需要根据不同项目、不同线体、不同命名习惯调整规则，就能得到更加贴近现场需要的结果。这使系统具备了较好的通用性与项目定制能力。</p><h2 id="8--web-">8. 本地 Web 交互设计</h2><ul><li>说明使用 Spring Boot 构建本地 Web 软件的原因，包括启动便捷、接口清晰、静态资源集成方便。</li><li>介绍用户操作流程：上传备份、读取配置、执行分析、查看结果、下载 Excel。</li></ul><h3 id="81--spring-boot--web-">8.1 为什么使用 Spring Boot 承载本地 Web 交互</h3><p>虽然系统的核心价值在于 KRL 解析，但若没有一个友好的交互外壳，解析结果就很难被现场用户充分利用。Spring Boot 在这里的角色并不是复杂业务平台，而是一个轻量、明确的本地接口与页面承载框架。它能够同时处理文件上传、配置读取、静态页面发布和结果下载，使本地程序具备类似网页应用的使用体验。</p><h3 id="82-">8.2 控制器与执行服务分层</h3><p>在本地模式下，前端主要通过三个接口与后端交互：<code>/api/config</code>、<code>/api/analysis</code> 和 <code>/api/analysis/excel</code>。其中，<code>AnalysisController</code> 负责参数接收和 HTTP 响应封装，而真正的同步分析流程由 <code>AnalysisExecutionService</code> 执行。这样的分层有助于控制器保持简洁，把文件校验、临时目录创建、上传文件落盘、调用核心分析服务和 Excel 导出等流程集中放到服务层处理。</p><h3 id="83-">8.3 用户交互流程</h3><p>用户打开程序后，首先可以读取当前磁盘上的配置，并在页面中查看或临时编辑。随后通过上传按钮选择一个或多个机器人备份 <code>zip</code> 文件。点击“开始分析”后，前端会把上传文件和当前配置文本以表单方式提交给 <code>/api/analysis</code>，后端返回 <code>RobotInfo</code> 列表。若用户需要保存结果，则可点击“下载 Excel”，由前端将相同输入提交给 <code>/api/analysis/excel</code> 并直接获取二进制文件。</p><p><strong>图片标题：图 3 本地 Web 用户操作流程图</strong></p><p><strong>图片内容描述：</strong> 图中展示用户从“打开程序”开始，依次执行“读取配置”“上传备份”“开始分析”“查看图谱结果”“下载 Excel”的流程，突出本地 Web 交互的线性操作路径。</p><h3 id="84-">8.4 页面结构组织</h3><p>前端页面由品牌区、顶部工具区、调用图画布、信息面板、视图切换器、节点控制区和 <code>Config</code> 编辑弹窗组成。其核心设计思路是：把高频操作集中在顶部，把结果呈现在中央画布，把辅助信息放在侧边或弹窗中，从而形成清晰的主次层次。这样既保留了网页操作的直观性，也兼顾了本地桌面使用时的效率。</p><h2 id="9-">9. 调用关系图可视化设计</h2><ul><li>介绍为何采用图结构展示调用关系，以及图形化结果在理解程序拓扑时的优势。</li><li>说明 Cytoscape 在节点布局、类型样式、交互切换和层级展示中的具体作用。</li><li>介绍线体信息视图与车型信息视图的设计思路。</li><li>介绍最终效果，如&quot;可以高亮查看调用链条和被调用链&quot;。</li></ul><h3 id="91-">9.1 为什么要使用图结构</h3><p>调用关系天然是图结构而不是普通表格结构。一个上层程序可能分派到多个车型分支，一个车型程序又可能调用多个轨迹程序，单纯用列表很难直观表达这种“多层连接”的关系。因此，图谱展示是理解程序拓扑最自然的方式。</p><h3 id="92-cytoscape-">9.2 Cytoscape 的角色</h3><p>Cytoscape 负责把后端返回的调用节点组织为可交互的图。前端会根据 <code>NodeType</code> 为不同节点赋予不同样式，例如颜色、形状、边框和尺寸，并结合布局算法把同层级节点尽可能稳定地分布在合理位置。用户不仅可以看到整体结构，还可以点击节点查看详情，并在视图中高亮相关调用链条，从而快速理解上游和下游关系。</p><h3 id="93-">9.3 两类核心视图</h3><p>当前页面主要提供两类视图。第一类是“线体信息”视图，它把每一个机器人备份作为一个根节点，用于帮助用户先在整条线体中定位目标机器人。第二类是“车型信息”视图，它会展开某一机器人内部的程序调用层级，让用户进一步观察 <code>Cell</code>、<code>P</code> 程序、车型码、车型程序和轨迹程序之间的关系。这样的双视图设计，可以把“先看整体，再钻取细节”的使用习惯自然落实到界面上。</p><h3 id="94-">9.4 信息侧栏与节点控制</h3><p>除了纯粹的图谱展示，前端还提供了信息面板和节点控制面板。信息面板用于显示机器人元信息、节点路径、时间和相关文本内容，帮助用户把抽象节点与实际程序文件对应起来。节点控制面板则允许按节点类型高亮或调整尺寸，使不同层级的结构差异更加清晰。</p><h2 id="10-excel-">10. Excel 导出方案设计</h2><ul><li>说明在线图谱虽然直观，但在存档、转发、跨平台查看方面存在局限。</li><li>介绍使用 Apache POI 生成 Excel 的原因及导出目标。</li><li>说明 Excel表相比在线图谱，无法直接表示&quot;图&quot;这种数据结构信息，难以醒目体现调用链和被调用链。于是有了Excel表格中树形结构区、调用矩阵区、颜色映射和层级表达的设计思路。</li></ul><h3 id="101--excel">10.1 为什么还需要 Excel</h3><p>网页图谱适合交互式查看，但在存档、汇报、跨部门转发和离线阅读场景中，Excel 更符合多数用户的使用习惯。因此，系统在设计之初就没有把在线图谱视为唯一输出，而是把 Excel 看作同等重要的结果载体。</p><h3 id="102-">10.2 独立导出服务的必要性</h3><p>Excel 导出由 <code>CallGraphExcelExportService</code> 独立承担，而没有直接写进控制器。这样做是因为导出逻辑本身就足够复杂：它不仅要遍历调用图，还要设计 Sheet 命名、单元格样式、列宽、合并关系、颜色区分和矩阵映射。把这部分逻辑独立出来，能够让控制器与分析服务继续保持单一职责。</p><h3 id="103-">10.3 树形区与调用矩阵区</h3><p>由于 Excel 本身不是图结构工具，直接把调用图“原样”搬进去并不可行。为了解决这一问题，导出方案采用了两部分结构。上半部分是树形结构区，按 <code>Cell 程序 -&gt; P 程序 -&gt; 车型代码 -&gt; 车型程序 -&gt; 轨迹程序</code> 的列层级展开调用路径，并通过合并单元格减少重复。下半部分是调用矩阵区，横向表示调用方，纵向表示被调用方，用于突出直接调用关系。前者便于顺着层级阅读，后者便于整体观察连接模式，两者结合后能够较好弥补 Excel 不擅长表达图结构的问题。</p><h3 id="104--sheet-">10.4 每机器人一 Sheet 的组织方式</h3><p>系统将每个机器人结果导出到一个独立 Sheet 中，而不是把所有机器人堆在同一张表里。这是因为机器人之间的程序集合、车型数量和轨迹规模都可能不同，若混在一起会明显降低可读性。以机器人为单位划分 Sheet，既符合前端线体视图的组织方式，也方便结果存档与人工核对。</p><p><strong>图片标题：图 4 调用关系图与 Excel 双输出示意图</strong></p><p><strong>图片内容描述：</strong> 图中左侧为网页中的调用关系图，右侧为 Excel 中的树形区与调用矩阵区，强调二者虽然展示形式不同，但都来自同一份结构化结果数据。</p><h2 id="11-">11. 程序整体流程说明</h2><ul><li>介绍包结构、核心类职责划分和模块边界设计。说明为什么要将语言解析能力与 Web 交互能力解耦。</li><li>按时间顺序介绍主要实现过程：读取 zip、构建模块、执行语法分析、抽取调用关系、按配置过滤、生成 JSON、前端渲染、导出 Excel。</li><li>说明各阶段的数据输入输出关系，形成完整的工程闭环。</li><li>分析配置管理、异常处理、日志记录和导出服务在工程中的组织方式。</li></ul><h3 id="111-">11.1 主要模块及职责划分</h3><p>从代码组织角度看，系统的大部分职责都可以落到几条主链上：<code>loader</code> 负责读取输入，<code>parser</code> 负责模块装配与程序分析，<code>service</code> 负责调用关系抽取和 Excel 导出，<code>pojo</code> 负责承载中间与最终数据结构，<code>krl-web</code> 则负责把核心能力转换为可操作的本地 Web 接口和页面。这种划分方式让系统形成了较清晰的工程边界。</p><p>表 4 概括了主要模块及其职责。</p><table><thead><tr><th> 模块或类 </th><th> 所属区域 </th><th> 主要职责 </th></tr></thead><tbody><tr><td> <code>KrlZipLoader</code> </td><td> <code>krl-core/loader</code> </td><td> 读取 zip、遍历文件、筛选并生成 <code>KrlFile</code> </td></tr><tr><td> <code>ModuleRepository</code> </td><td> <code>krl-core/parser</code> </td><td> 耦合 <code>.src/.dat</code>，建立模块与 callable 索引 </td></tr><tr><td> <code>ModuleParser</code> </td><td> <code>krl-core/parser</code> </td><td> 调用 ANTLR4 分析器解析单个模块 </td></tr><tr><td> <code>CarCallReferenceAnalyze</code> </td><td> <code>krl-core/parser</code> </td><td> 抽取调用关系并构造层级图 </td></tr><tr><td> <code>CarCallAnalysisService</code> </td><td> <code>krl-core/service</code> </td><td> 串联批量分析流程并生成 <code>RobotInfo</code> </td></tr><tr><td> <code>CallGraphExcelExportService</code> </td><td> <code>krl-core/service</code> </td><td> 将调用图导出为 Excel </td></tr><tr><td> <code>ConfigStorageService</code> </td><td> <code>krl-web/config</code> </td><td> 负责磁盘配置初始化、读取和临时配置解析 </td></tr><tr><td> <code>AnalysisExecutionService</code> </td><td> <code>krl-web/service</code> </td><td> 承接本地同步分析与导出流程 </td></tr><tr><td> <code>AnalysisController</code> </td><td> <code>krl-web/controller</code> </td><td> 提供配置读取、分析和 Excel 下载接口 </td></tr></tbody></table><h3 id="112-">11.2 输入到输出的完整链路</h3><p>从时间顺序上看，程序运行过程可以描述为以下几个步骤：</p><ol start="1"><li>用户上传一个或多个机器人备份 <code>zip</code> 文件。</li><li>后端将上传文件保存到临时目录，并根据当前配置读取文件内容。</li><li><code>KrlZipLoader</code> 遍历压缩包，筛选有效文件并生成 <code>KrlFile</code> 列表。</li><li><code>ModuleRepository</code> 按模块名耦合 <code>.src</code> 与 <code>.dat</code>，形成 <code>KrlModule</code> 集合。</li><li><code>ModuleParser</code> 调用 ANTLR4 生成的分析器，对模块进行语法解析。</li><li><code>CarCallReferenceAnalyze</code> 根据程序结构恢复车型调用关系。</li><li><code>CarCallAnalysisService</code> 结合 <code>am.ini</code> 等信息补充机器人元数据，生成 <code>RobotInfo</code> 列表。</li><li>若是网页查看，则结果被序列化为 JSON 返回前端并由 Cytoscape 渲染。</li><li>若是 Excel 导出，则结构化结果被送入 <code>CallGraphExcelExportService</code> 生成工作簿字节流。</li></ol><p>这一链路的关键特点在于，各阶段输入输出边界清晰。文件读取阶段输出 <code>KrlFile</code>，模块装配阶段输出 <code>KrlModule</code>，调用关系分析阶段输出 <code>RobotInfo</code> 与 <code>CallNode</code>，前端和导出阶段都以同一结构化结果为输入。这样既避免了重复转换，也使系统更容易定位问题。</p><h3 id="113-">11.3 配置、异常与日志在工程中的位置</h3><p>配置管理、异常处理和日志记录并不直接产生调用关系，但它们决定了程序是否真正可用。配置管理保证系统能够在不同项目间快速调整规则。异常处理保证上传错误、配置错误、解析错误和导出错误能够被明确识别，而不是以模糊失败形式呈现。日志记录则帮助开发和维护人员追踪分析流程，理解每一步的输入输出规模和失败位置。对于一款面向实际生产支持的软件而言，这些看似“外围”的工程能力同样是完整实现的一部分。</p><h2 id="12-">12. 总结</h2><ul><li>总结本文的工程设计方法与实际实现成果。</li><li>总结程序在降低人工分析成本、提升问题定位效率、辅助知识沉淀方面的作用。</li></ul><p>本文在第一篇 KRL 文法分析研究的理论基础上，围绕车型调用关系这一明确目标，完成了一套本地 Web 软件的工程实现说明。从机器人备份文件读取，到 <code>.src</code> 与 <code>.dat</code> 的模块耦合，再到 ANTLR4 语法分析、调用关系抽取、配置过滤、图谱展示和 Excel 导出，系统建立了一条从原始备份到可读结果的完整处理链。</p><p>从工程方法上看，本文的核心不是简单堆叠技术组件，而是把不同层次的问题分别交给合适的模块处理：文件层解决输入组织问题，模块层解决程序归属问题，语法层解决结构识别问题，业务层解决调用关系恢复问题，交互层解决结果表达问题。这种分层使系统既具备可维护性，也具备后续扩展空间。</p><p>从实际价值上看，该程序能够显著降低人工分析机器人程序的重复劳动，帮助调试人员和维护人员更快定位车型调用链，辅助工艺问题排查，并为项目交接与知识沉淀提供更加稳定的载体。对于一项直接服务现场的工程工具而言，能够把复杂程序结构转化为用户易于理解和传播的结果，本身就是其最重要的成果。</p><h2 id="">参考资料</h2><ul><li>项目代码：<code>/Users/liuke/IdeaProjects/KRLParser/krl-core</code></li><li>项目代码：<code>/Users/liuke/IdeaProjects/KRLParser/krl-web</code></li><li>KRL 文法文件：<code>/Users/liuke/IdeaProjects/KRLParser/krl-core/src/main/resources/krl.g4</code></li><li>关键实现类：<code>/Users/liuke/IdeaProjects/KRLParser/krl-core/src/main/java/tech/waitforu/loader/KrlZipLoader.java</code></li><li>关键实现类：<code>/Users/liuke/IdeaProjects/KRLParser/krl-core/src/main/java/tech/waitforu/parser/ModuleRepository.java</code></li><li>关键实现类：<code>/Users/liuke/IdeaProjects/KRLParser/krl-core/src/main/java/tech/waitforu/parser/CarCallReferenceAnalyze.java</code></li><li>关键实现类：<code>/Users/liuke/IdeaProjects/KRLParser/krl-web/src/main/java/tech/waitforu/krlweb/controller/AnalysisController.java</code></li></ul></div><p style="text-align:right"><a href="https://waitforu.tech/posts/develope/krlParserdocsdevInfo#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://waitforu.tech/posts/develope/krlParserdocsdevInfo</link><guid isPermaLink="true">https://waitforu.tech/posts/develope/krlParserdocsdevInfo</guid><dc:creator><![CDATA[Cody]]></dc:creator><pubDate>Tue, 10 Mar 2026 16:41:12 GMT</pubDate></item><item><title><![CDATA[KRL文法规则及其编译原理分析]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://waitforu.tech/posts/develope/krl">https://waitforu.tech/posts/develope/krl</a></blockquote><div><h1 id="krl">KRL文法规则及其编译原理分析</h1><h2 id="">摘要</h2><p>KUKA 工业机器人广泛应用于焊接、搬运、涂胶等自动化场景，其控制程序通常以 KRL 语言编写，并随机器人备份文件一并保存。随着车型种类增多、车型数量增加，依靠现场专业人员逐个翻阅示教器或备份文件来判断机器人程序结构和调用关系的方式，已经难以满足效率与准确性的双重要求。基于这一现实背景，本文从编译原理角度出发，讨论对 KRL 程序进行自动化静态分析的可行性，并围绕 ANTLR4 的 <code>LL(*)</code> 分析策略，对 KRL 的主要语法结构进行整理与建模。</p><p>本文不以“实现一个完整编译器”为目标，而是面向静态程序理解任务，重点研究如何通过词法分析与语法分析，从 KRL 文本中提取稳定、可结构化的语法信息。围绕 <code>krl.g4</code> 文法文件，本文系统说明了 KRL 中注释、控制头、标识符、字面量、程序定义、控制流语句、运动语句、表达式与调用表达式等内容如何被整理为适合自动分析的形式，并解释了若干关键改写策略，例如大小写不敏感处理、关键字与标识符冲突处理、表达式优先级组织，以及 <code>.src</code> 与 <code>.dat</code> 双入口统一建模等。</p><p>研究表明，KRL 程序虽然具有工业控制语言特有的业务语义和项目约定性，但其文本形式仍然具有较强的结构化特征，完全可以借助编译原理方法建立自动化分析基础。特别是，当 KRL 语言特征被整理为满足 ANTLR4 要求的文法规则后，词法分析器与语法分析器便能够稳定地产生结构化结果，为后续的调用关系识别、程序理解和可视化分析提供统一入口。本文最终的主要理论产出，是一份能够描述 KRL 核心语法结构、并能支撑后续静态分析的 <code>krl.g4</code> 文法规则体系。</p><p><strong>关键词：</strong> KRL；ANTLR4；词法分析；语法分析；LL(*)；工业机器人程序；静态分析</p><blockquote><p>说明：本篇报告对kuka工业机器人所使用的编程语言KRL进行梳理，并编写其所对应的满足LL(*)要求的文法规则。随后基于分析Kuka机器人程序的需求，结合编译原理的相关理论，给出解析KRL程序的方案，并做可行性分析。</p></blockquote>
<h2 id="1-">1. 背景介绍</h2><ul><li>目前对于kuka工业机器人设备中运行程序的梳理依赖于人力。只有经过培训的相关人员才能够&quot;读懂&quot;程序。</li><li>项目调试与生产中存在大量场景依赖于&quot;了解&quot;工业机器人所运行的程序。</li><li>数量众多、车型繁杂导致仅依靠人力来获取当前生产线体的情况困难重重。</li><li>由于近年来标准化的有序推进、调试规范的充分应用，自动化分析程序具备了条件。</li></ul><p>在当前工业现场中，机器人程序往往由调试工程师和设备维护人员分阶段维护。对于熟悉某条产线历史的人来说，理解某个程序为何调用另一个程序，某个车型分支为何对应某套轨迹，往往依靠经验积累即可完成；但一旦人员流动、项目交接或车型快速切换，仅靠人工梳理机器人程序就会变得十分困难。尤其是在 KUKA 机器人项目中，程序数量多、命名跨度大、调用层级深，人工排查不仅耗时，还容易受个人经验影响而产生遗漏。</p><p>过去难以自动化，主要原因并不是 KRL 完全不可分析，而是项目现场的程序组织不够规范，命名习惯和调用方式差异较大。近年来，随着标准化命名、模板化程序框架、固定备份结构和统一调试规范逐步普及，工业机器人程序已经具备了较稳定的结构特征。这意味着，基于程序文本本身开展静态分析，从“人工经验驱动”转向“规则与语法驱动”，已经具备现实条件。</p><p><strong>图片标题：图 1 KRL 程序静态分析总体思路图</strong></p><p><strong>图片内容描述：</strong> 图中从“机器人备份文件”出发，依次经过“提取 KRL 源文本”“词法分析”“语法分析”“结构化理解”“后续进一步分析与组合”几个阶段，突出本文关注的是前半部分的理论基础。</p><h2 id="2-">2. 问题定义与研究目标</h2><ul><li>明确本文的核心议题：通过对KRL语法规则的分析，对编译原理的研究，为自动化分析程序提供理论基础。</li><li>说明研究目标不是执行程序，而是静态分析程序结构，因此并不需要完整制作出一个编译器。</li><li>界定分析范围、输入对象(程序字符串)与预期输出结果(所需相关数据)。</li></ul><p>本文研究的问题可以概括为：面对 KUKA 工业机器人备份中的 KRL 程序文本，是否能够通过编译原理中的词法分析与语法分析方法，将其转化为适合后续静态分析的结构化表示，从而为调用关系识别奠定基础。</p><p>这里需要强调的是，本文并不追求实现一个完整的 KRL 编译器。完整编译器的目标是将源程序转为目标程序，或驱动解释执行；而本项目面对的任务是程序理解，其核心目标是“读懂程序结构”，而不是“运行程序语义”。换言之，本文讨论的是一种面向静态分析的语言处理过程，其重点在于识别程序的形式结构，例如程序定义、语句块、控制流、调用表达式等。</p><p>从输入角度看，本文的分析对象是 KRL 程序字符串，主要来自机器人备份中的 <code>.src</code> 与 <code>.dat</code> 文件；从输出角度看，本文不直接输出最终的调用图，而是输出能够支撑后续调用关系识别的结构化语法基础。这一基础最直接的体现，就是 <code>krl.g4</code> 所描述的文法体系，以及据此能够生成的词法分析器与语法分析器。</p><p>表 1 对本文的研究对象和预期产出做了概括。</p><table><thead><tr><th> 项目       </th><th> 内容                                                   </th></tr></thead><tbody><tr><td> 输入对象   </td><td> 机器人备份中的 KRL 源程序文本（主要为 <code>.src</code>、<code>.dat</code>） </td></tr><tr><td> 研究目标   </td><td> 建立适合自动分析的 KRL 文法规则与语法解释体系          </td></tr><tr><td> 不做的内容 </td><td> 程序执行、控制器仿真、完整编译、在线控制               </td></tr><tr><td> 直接产出   </td><td> <code>krl.g4</code> 文法文件与可解释的词法/语法分析框架           </td></tr><tr><td> 间接价值   </td><td> 为后续调用关系识别、结构提取、可视化分析提供基础       </td></tr></tbody></table><h2 id="3-">3. 工业机器人程序调用关系识别的可行性分析</h2><ul><li>从原理上论证 KRL 程序是一种结构化工业控制语言，存在清晰的语法边界和调用语句模式，因此具备静态分析基础。</li><li>从数据来源上论证机器人备份文件中包含足够的源代码、配置和元信息，可支撑调用关系恢复。</li><li>从成本上分析使用自动化语法分析替代人工阅读的必要性与经济性。</li><li>从编译原理角度，对于字符串进行一系列操作是可以得到该文本所蕴含的&quot;信息&quot;。</li><li>从工程实现上说明已有工具链可以进行辅助，如利用Antlr4来进行词法分析和语法分析。</li></ul><h3 id="31-">3.1 原理上的可行性</h3><p>KRL 并不是自然语言，也不是完全自由的脚本语言，它具有明显的结构性：程序由关键字定义边界，语句具有固定的组合模式，控制流依赖 <code>IF</code>、<code>SWITCH</code>、<code>FOR</code>、<code>LOOP</code> 等显式结构，过程和函数调用通常也以明确的调用语句形式出现。这意味着，只要能够正确识别关键字、标识符、字面量和语句边界，就有机会把原本连续的文本整理成可分析的结构。</p><p>从编译原理角度看，程序文本中的“信息”并不是天然不可见的。所谓静态分析，本质上就是先把字符流切分为 token，再把 token 组合成语法结构，最后基于这些结构进行进一步的语义判断。因此，只要 KRL 程序具有稳定的词法和语法模式，就可以通过语言处理方法把隐藏在文本中的程序结构提取出来。</p><h3 id="32-">3.2 数据来源上的可行性</h3><p>KUKA 机器人备份文件一般保留了源代码、配置文件和一定的元信息。对于本文关注的静态分析任务来说，最关键的是 <code>.src</code> 和 <code>.dat</code> 文件中包含了程序主体、变量声明、过程/函数定义以及调用相关文本。这些内容并非黑盒二进制，而是可读取、可解析的文本资源。</p><p>此外，备份文件通常保留原始文件路径、文件名和程序分布方式，使得“模块归属”和“文件组织方式”也能被纳入分析。这一点对于后续理解程序结构非常关键，因为工业机器人程序并不只是单文件语言，它往往依赖文件层级和项目命名习惯共同组织语义。</p><h3 id="33-">3.3 成本上的可行性</h3><p>人工阅读 KRL 程序的主要问题，不是“完全做不到”，而是“规模上不经济”。一个或几个程序靠人工理解尚可完成，但当同一线体下机器人数量上升、车型分支增多、项目版本累积后，人工方式的重复劳动迅速增加。自动化分析并不要求替代全部人工判断，只要能把大部分结构关系快速梳理出来，就足以显著降低现场人员的理解成本。</p><p>从研发成本看，直接使用正则表达式进行大量特征匹配虽然上手快，但后续维护困难；完全手写解析器则实现复杂、验证成本高。相比之下，使用成熟的语法分析工具来承接 KRL 语言整理工作，在成本与收益之间更平衡。</p><h3 id="34-">3.4 工具链上的可行性</h3><p>ANTLR4 已经是成熟的语法分析工具。它能够根据文法文件自动生成词法分析器与语法分析器，并提供解析树访问机制，这使得研究重点可以从“如何手写解析器”转向“如何准确表达 KRL 文法”。对本文而言，这种工具链的存在极大降低了工程门槛，使得问题的核心转化为：如何为 KRL 设计出一份可被 <code>LL(*)</code> 分析器接受、又足够贴合工业现场实际的文法。</p><p>表 2 给出了三种常见方案的对比。</p><table><thead><tr><th> 方案                </th><th> 优点                                 </th><th> 局限                                   </th><th> 适合程度 </th></tr></thead><tbody><tr><td> 正则匹配            </td><td> 上手快，适合局部模式识别             </td><td> 难以稳定处理嵌套结构、优先级和复杂语句 </td><td> 低       </td></tr><tr><td> 手写递归解析        </td><td> 可高度定制                           </td><td> 开发与维护成本高，正确性验证困难       </td><td> 中       </td></tr><tr><td> ANTLR4 文法驱动解析 </td><td> 结构清晰，便于维护与扩展，工具链成熟 </td><td> 前期需要认真整理文法                   </td><td> 高       </td></tr></tbody></table><h2 id="4-krl-">4. KRL 语言特征与分析难点</h2><ul><li>概述 KRL 的文件组织方式、程序声明方式和典型调用写法。</li><li>说明 <code>.src</code>、<code>.dat</code> 分离、内联注释、系统指令、宏折叠语句等语言特征对语法分析带来的影响。</li><li>分析调用识别中的主要难点，如大小写不敏感、语法片段不完全标准化、备份文件结构复杂、业务语义强依赖项目约定等。</li></ul><p>KRL 是一种典型的工业控制语言。它既有类似传统编程语言的程序定义、条件判断、循环和函数调用，也有大量与机器人控制密切相关的运动语句、模拟量控制语句、中断语句和工艺专用表达方式。因此，它并不是“语法极其简单的配置语言”，也不是可以完全照搬通用语言分析思路的高级语言。</p><p>KRL 的第一个显著特点，是 <code>.src</code> 与 <code>.dat</code> 的分离。<code>.src</code> 更偏向可执行程序主体，<code>.dat</code> 则承担变量、常量、枚举、结构体等数据定义。二者共同组成一个模块。这种文件组织方式要求分析器不能只看单个文件，而必须理解“一个模块可由两个文件共同构成”这一事实。</p><p>第二个特点是，KRL 文件头部常出现若干以 <code>&amp;</code> 开头的控制行，例如版本、模板、磁盘路径和程序注释等。这些控制行不是普通可执行语句，但又真实存在于源文件文本中。如果忽略不当，就会干扰正常语法分析；如果完全丢弃，又会失去部分有价值的元信息。因此，如何对这类内容进行单独建模，是 KRL 文法设计的一个重要问题。</p><p>第三个特点是，KRL 在工业现场中具有较强的“项目约定性”。例如某些程序名承担特定角色，某些 <code>SWITCH</code> 结构被约定用来按程序号或车型号进行分派，某些轨迹调用遵循特定命名形式。也就是说，KRL 的形式语法虽然能描述“什么写法是合法的”，但程序真正的业务含义往往还依赖行业约定和项目规范。这也是本文后续强调“语法分析是基础，而不是全部”的原因。</p><p>第四个特点是大小写不敏感。对于人来说，这通常只是书写习惯问题；但对于自动分析器来说，这意味着词法规则不能简单按大小写严格匹配，否则同样的关键字会被错误地视为不同 token。与之类似的还有“某些关键字在某些上下文中又能当作标识符使用”的现象，这也是 KRL 在文法设计上比表面更复杂的原因。</p><p><strong>图片标题：图 2 <code>.src</code> 与 <code>.dat</code> 文件组织关系示意图</strong></p><p><strong>图片内容描述：</strong> 图中展示一个模块由同名 <code>.src</code> 和 <code>.dat</code> 文件共同组成，<code>.src</code> 保存程序体，<code>.dat</code> 保存数据定义，并通过模块名与文件名建立对应关系。</p><h2 id="5-">5. 编译原理基础与本文方法选择</h2><ul><li>说明词法分析、语法分析、语义分析在静态程序理解中的作用。</li><li>解释为什么本项目采用“词法分析 + 语法分析 + 语义遍历”的经典编译技术路线。</li><li>对比正则匹配、手写递归解析和 ANTLR4 自动生成分析器的优劣，论证采用 ANTLR4 的合理性。</li></ul><p>编译原理通常被理解为“编译器如何把高级语言变成机器代码”的理论，但它的核心价值其实更广。对于本文关心的静态分析问题而言，编译原理提供的是一套把源程序文本转为结构化表示的可靠方法。即便最终并不生成目标代码，词法分析、语法分析和语义分析仍然是理解程序结构的自然路径。</p><p>词法分析解决的是“切词”问题，即把连续字符流切分成有意义的最小单位，例如关键字、标识符、字面量、操作符和分隔符。语法分析解决的是“组句”问题，即判断这些 token 如何组成更高层次的结构，例如过程定义、条件语句、循环语句和表达式。语义分析则进一步讨论这些结构在具体任务中的含义，例如某个调用表达式是否表示一次真正的程序调用，某个分支结构是否承担车型分派作用。</p><p>本文之所以采用“词法分析 + 语法分析 + 后续语义理解”的路线，是因为这种路线与问题本身高度匹配。当前任务不是简单从文本里搜若干关键字，而是要稳定识别程序结构。如果跳过词法和语法阶段，直接用字符串或正则处理，很快就会遇到嵌套结构、优先级、分支语句和多义表达的问题。</p><p>ANTLR4 的优势在于，它把“语言规则的描述”与“分析器的生成”清晰分离。开发者不需要手工编写大量状态转移逻辑，只需把语言结构准确写成文法规则，即可生成对应的 <code>Lexer</code> 和 <code>Parser</code>。对本文来说，这意味着工作重心不是“实现一个分析器框架”，而是“把 KRL 语言整理成可以被形式化描述的文法”。</p><p><strong>图片标题：图 3 ANTLR4 的词法分析与语法分析链路图</strong></p><p><strong>图片内容描述：</strong> 图中依次展示字符流进入词法分析器形成 token 序列，再进入语法分析器形成语法树，最后为后续静态分析任务提供结构化输入。</p><h2 id="6--ll--krl-">6. 面向 LL(*) 的 KRL 文法建模思路</h2><ul><li>说明 ANTLR4 基于 LL(*) 分析策略，对文法左递归、歧义性和上下文依赖有一定要求。</li><li>介绍如何将 KRL 的主要语法结构抽象为可被 LL(*) 接受的文法规则。</li><li>重点说明对程序定义、调用语句、条件分支、循环语句、参数列表等结构的规则改写思路。</li><li>说明如何通过文法拆分、优先级控制、可选项约束等方式降低歧义。</li></ul><p>ANTLR4 采用的是 <code>LL(*)</code> 风格的自顶向下分析策略。对实际工程来说，这意味着文法设计要尽量清晰地体现“从前往后读、逐层做选择”的结构，而不能把大量含糊或高度依赖上下文的写法直接堆在一起。如果文法规则之间区分不清，分析器就容易出现歧义、回溯复杂或难以维护的问题。</p><p>因此，KRL 文法整理的关键，并不是机械照抄语言手册，而是把工业现场中的真实写法转换为更适合形式描述的结构。以程序入口为例，KRL 文件并非只有一种形式，<code>.src</code> 与 <code>.dat</code> 对应两种不同内容；以表达式为例，不同运算之间存在优先级和结合性关系；以调用表达式为例，变量访问、结构体成员访问和函数调用在文本上相似，但在语法角色上并不相同。如果这些层次不加区分，后续分析就会变得混乱。</p><p>本文在建模时采用了几项基本原则。第一，优先按语言层次拆分规则，例如将“文件入口”“程序定义”“语句”“表达式”“字面量”分层组织。第二，优先把差异大的结构分开建模，例如过程与函数、数据文件与代码文件、变量访问与调用表达式。第三，对存在可选项或多种变体的结构，尽量通过规则拆分和局部约束降低歧义，而不是在单条规则中无序堆叠。</p><p>这种整理思路最终体现在 <code>krl.g4</code> 中。它不是对 KRL 语言的百科全书式收录，而是一份面向静态分析任务的核心文法。换言之，文法设计的目标不是覆盖所有极少见的历史写法，而是在满足主要工业场景的前提下，提供稳定、可读、可维护的解析基础。</p><h2 id="7-krlg4">7. 词法分析器设计(重点篇章，需结合已编写的krl.g4进行举例展示)</h2><ul><li>说明需要识别的基本记号类别，如关键字(分类整理好列出)、标识符、字面量(数字、字符串)、注释、系统指令与分隔符。</li><li>介绍大小写不敏感处理策略以及对 KRL 特殊词法现象的兼容方式。</li><li>说明词法分析阶段如何为后续语法分析提供稳定的 token 序列。</li></ul><h3 id="71-">7.1 词法分析器的任务</h3><p>如果把 KRL 源程序看作一整段连续文本，那么词法分析器的工作，就是把这段文本切分成一个个“有名字的片段”。例如 <code>DEF</code> 应识别为过程定义关键字，<code>P13</code> 应识别为标识符，<code>123</code> 应识别为整数字面量，<code>;注释</code> 应识别为注释，<code>
</code> 应识别为换行。</p><p>只有完成这一步，语法分析器才能继续判断：“这些 token 究竟组成了一个过程定义、一个循环语句，还是一次调用表达式”。因此，词法层的目标不是理解程序含义，而是给后续语法层提供稳定而一致的原材料。</p><h3 id="72-krlg4-">7.2 <code>krl.g4</code> 中词法规则的整体组织方式</h3><p>在 <code>krl.g4</code> 中，词法规则位于文件后半部分，采用大写命名。该文件首先定义了特殊文本单元，例如注释与控制行；随后定义各种关键字 token；再定义空白与换行；最后定义字面量、标识符和一组用于大小写不敏感匹配的 <code>fragment</code> 规则。这样的组织方式符合 KRL 的实际需要：先处理高优先级、容易与普通标识符冲突的特殊结构，再处理通用规则。</p><p>读者在阅读 <code>krl.g4</code> 的 lexer 部分时，可以按以下顺序理解：</p><ol start="1"><li>先看“哪些内容被单独切出来”，如注释与控制行；</li><li>再看“哪些单词被定义为关键字”，如 <code>DEF</code>、<code>IF</code>、<code>SWITCH</code>；</li><li>再看“哪些内容属于字面量”，如整数、实数、字符串、字符；</li><li>最后看“剩余普通名字如何识别”，即 <code>IDENTIFIER</code> 以及大小写不敏感片段。</li></ol><h3 id="73-">7.3 注释与控制行：为什么必须单独建模</h3><p>KRL 中以分号 <code>;</code> 开头的是行内注释，而以 <code>&amp;</code> 开头的内容则是控制行，例如版本号、模板路径、磁盘路径等。它们都真实存在于源程序文件里，但作用不同。</p><p>在 <code>krl.g4</code> 中，注释规则写成如下形式：</p><pre class="language-antlr lang-antlr"><code class="language-antlr lang-antlr">Comment
    : &#x27;;&#x27; (~(&#x27;\n&#x27; | &#x27;\r&#x27;))* -&gt; channel(HIDDEN)
    ;
</code></pre>
<p>这里最关键的不是“分号后面跟随到行尾”，而是 <code>-&gt; channel(HIDDEN)</code>。这表示：注释会被词法分析器识别出来，但不会进入语法分析器默认消费的 token 通道。这样设计有两个好处。第一，注释不会干扰正常语法匹配；第二，注释并没有被彻底丢弃，后续如果有需要，仍然可以从隐藏通道中取回。对静态分析而言，这是一种很实用的折中方式。</p><p>控制行则采用另一种处理方式：</p><pre class="language-antlr lang-antlr"><code class="language-antlr lang-antlr">KrlControlLine
    : &#x27;&amp;&#x27; (~(&#x27;\n&#x27; | &#x27;\r&#x27;))*
    ;
</code></pre>
<p>控制行没有被直接跳过，而是保留为一种独立 token。这是因为控制行虽然不属于普通可执行语句，但它在文件中往往承担元信息作用，例如记录程序模板或访问权限。把它单独建模，既避免它与普通语句混淆，也为后续保留了解释空间。</p><h3 id="74-">7.4 关键字：不是简单罗列，而是功能分组</h3><p>KRL 关键字很多，但若只是把它们逐个列出来，读者很难建立整体认识。因此，更合理的方式是按功能分组理解。</p><p>表 3 给出了 <code>krl.g4</code> 中主要词法记号的分类方式。</p><table><thead><tr><th> 记号类别     </th><th> 代表规则                                                     </th><th> 作用说明                         </th></tr></thead><tbody><tr><td> 程序定义类   </td><td> <code>DEF</code>、<code>DEFFCT</code>、<code>DEFDAT</code>、<code>END</code>、<code>ENDFCT</code>、<code>ENDDAT</code>         </td><td> 定义过程、函数、数据文件及其边界 </td></tr><tr><td> 控制流类     </td><td> <code>IF</code>、<code>ELSE</code>、<code>ENDIF</code>、<code>FOR</code>、<code>LOOP</code>、<code>SWITCH</code>、<code>CASE</code>、<code>DEFAULT</code> </td><td> 描述程序流程结构                 </td></tr><tr><td> 调用与中断类 </td><td> <code>EXT</code>、<code>EXTFCT</code>、<code>INTERRUPT</code>、<code>TRIGGER</code>、<code>RETURN</code>            </td><td> 描述调用声明、中断和触发机制     </td></tr><tr><td> 运动指令类   </td><td> <code>PTP</code>、<code>LIN</code>、<code>CIRC</code>、<code>LIN_REL</code>、<code>SPTP</code>、<code>SLIN</code>              </td><td> 描述机器人运动行为               </td></tr><tr><td> 输入输出类   </td><td> <code>SIGNAL</code>、<code>ANIN</code>、<code>ANOUT</code>、<code>WAIT</code>、<code>ON</code>、<code>OFF</code>               </td><td> 描述信号与等待、模拟量控制       </td></tr><tr><td> 数据定义类   </td><td> <code>DECL</code>、<code>ENUM</code>、<code>STRUC</code>、<code>GLOBAL</code>、<code>CONST</code>、<code>PUBLIC</code>         </td><td> 描述变量、枚举、结构体和修饰符   </td></tr><tr><td> 字面量类     </td><td> <code>INTLITERAL</code>、<code>REALLITERAL</code>、<code>STRINGLITERAL</code>、<code>CHARLITERAL</code>  </td><td> 承载基础数值和文本               </td></tr><tr><td> 标识符类     </td><td> <code>IDENTIFIER</code>、<code>krlIdentifier</code>                                </td><td> 表示模块名、变量名、程序名等     </td></tr></tbody></table><p>这样分组后，读者就能明白：词法分析器不是无差别切词，而是在尽量把不同语言角色分离出来，为后续语法分析创造条件。</p><h3 id="75-">7.5 大小写不敏感：为什么在词法层解决</h3><p>KRL 的一个典型特征是大小写不敏感。也就是说，<code>DEF</code>、<code>def</code>、<code>Def</code> 在语言语义上通常等价。如果词法规则只按某一种大小写形式定义，那么现实中的大量程序都无法被稳定识别。</p><p><code>krl.g4</code> 采用了一种直接而清晰的方案：为字母 A 到 Z 分别定义 <code>fragment</code> 规则，例如：</p><pre class="language-antlr lang-antlr"><code class="language-antlr lang-antlr">fragment A : (&#x27;a&#x27; | &#x27;A&#x27;);
fragment D : (&#x27;d&#x27; | &#x27;D&#x27;);
fragment E : (&#x27;e&#x27; | &#x27;E&#x27;);
fragment F : (&#x27;f&#x27; | &#x27;F&#x27;);
</code></pre>
<p>在此基础上，关键字规则写成：</p><pre class="language-antlr lang-antlr"><code class="language-antlr lang-antlr">DEF : D E F;
</code></pre>
<p>这样一来，无论源程序写成 <code>DEF</code>、<code>def</code> 还是 <code>DeF</code>，都能被识别为同一个 token。把大小写兼容放在词法层解决，有两个优点。第一，语法层无需反复考虑大小写问题；第二，关键字与标识符的识别规则能保持一致性。</p><h3 id="76--krlidentifier">7.6 为什么有些关键字又被纳入 <code>krlIdentifier</code></h3><p>KRL 的一个特殊现象在于，有些词在某些上下文中是关键字，在另一些上下文中又可能出现在标识符位置。例如 <code>SEC</code> 既能出现在 <code>WAIT SEC 10</code> 中表示时间单位，也可能在结构体成员访问中作为普通名字出现。类似地，<code>WITH</code>、<code>DELAY</code>、<code>DISTANCE</code> 等词也可能同时扮演不同角色。</p><p>因此，<code>krl.g4</code> 没有简单地把“关键字”和“标识符”完全对立，而是额外定义了 <code>krlIdentifier</code> 这一语法层概念，将 <code>IDENTIFIER</code> 与若干特定关键字共同纳入：</p><pre class="language-antlr lang-antlr"><code class="language-antlr lang-antlr">krlIdentifier
    : IDENTIFIER
    | DISTANCE
    | STEP
    | SEC
    | WITH
    | ASYCANCEL
    | BRAKE
    | DELAY
    | DO
    | MAXIMUM
    | MINIMUM
    ...
</code></pre>
<p>这并不是词法层的混乱，而是一种对 KRL 语言现实的适配。换句话说，词法分析器先把这些词识别为关键字 token，而语法层再根据上下文决定：它们在此处究竟承担关键字功能，还是可以视为标识符的一部分。这样处理，既保留了词法分类的清晰性，又兼顾了 KRL 写法的灵活性。</p><h3 id="77-">7.7 字面量与普通标识符：让源程序中的值“有类型地进入分析器”</h3><p><code>krl.g4</code> 中对数字、字符、字符串的定义较完整。例如 <code>REALLITERAL</code> 支持科学计数法，<code>INTLITERAL</code> 不仅支持十进制，还支持 KRL 常见的十六进制与二进制写法；<code>CHARLITERAL</code> 与 <code>STRINGLITERAL</code> 则通过转义序列规则处理特殊字符。</p><p>这类规则的重要性在于，它们让源程序中的“值”能够带着形式信息进入语法层。例如，程序里写的是普通整数，还是带指数的实数，还是枚举字面量，这些差异在后续结构理解中都有价值。即使本文不深入讨论完整语义求值，词法阶段仍然需要把这些不同形式的文本准确区分开来。</p><p>普通名字则由 <code>IDENTIFIER</code> 负责识别：</p><pre class="language-antlr lang-antlr"><code class="language-antlr lang-antlr">IDENTIFIER
    : [a-zA-Z_$][a-zA-Z_$0-9]*
    ;
</code></pre>
<p>这个规则说明，KRL 中的变量、模块、函数、过程名称不能以数字开头，但可以包含字母、数字、下划线和 <code>$</code>。这与工业现场中大量系统变量、信号变量的书写方式是相吻合的。</p><h3 id="78-">7.8 空白、换行与词法顺序的重要性</h3><p>在普通高级语言中，换行有时只是排版问题；但在 KRL 中，很多规则以“换行结束一条语句”为隐含边界。因此，<code>NEWLINE</code> 并没有被简单忽略，而是作为一种明确的 token 保留：</p><pre class="language-antlr lang-antlr"><code class="language-antlr lang-antlr">WS      : (&#x27; &#x27; | &#x27;\t&#x27; | &#x27;\u000C&#x27;) -&gt; skip;
NEWLINE : &#x27;\r&#x27;? &#x27;\n&#x27;;
</code></pre>
<p>这里的设计含义非常明确：空格和制表符对大多数语法结构没有决定性作用，因此可直接跳过；而换行会影响语句边界和程序结构，因此必须保留到语法层。</p><p>同时，阅读 lexer 规则时还要注意一个原则：<strong>词法规则的定义顺序会影响匹配结果</strong>。这也是为什么 <code>krl.g4</code> 先定义大量具体关键字，再定义较宽泛的 <code>IDENTIFIER</code>。如果把通用标识符规则放在前面，许多本应识别为关键字的文本就可能先被吃掉，导致后续语法分析混乱。</p><h3 id="79--krlg4--lexer-">7.9 本章小结：如何阅读 <code>krl.g4</code> 的 lexer 部分</h3><p>读者在阅读 <code>krl.g4</code> 的词法层时，可以按以下顺序把握其含义：</p><ol start="1"><li>先找特殊文本单元：注释、控制行、换行、空白；</li><li>再找被明确列出的关键字；</li><li>再看各种字面量如何定义；</li><li>最后看普通标识符与大小写不敏感片段。</li></ol><p>一旦理解了这一组织方式，读者就会发现，<code>krl.g4</code> 的 lexer 部分并不是机械罗列单词，而是在为后续语法结构搭建稳定、可控的 token 基础。</p><h2 id="8-krlg4">8. 语法分析器设计(重点篇章，需结合已编写的krl.g4进行举例展示)</h2><ul><li>说明如何围绕程序声明、过程(Process)/函数(Function)的调用、条件流程和语句块等构建语法树。</li><li>介绍程序主体、声明区、执行语句区在语法树中的组织方式。</li><li>说明语法树生成后，为什么可以为后续调用关系提取提供清晰的结构化基础。</li></ul><h3 id="81-">8.1 语法分析器的任务</h3><p>如果说词法分析器负责切分 token，那么语法分析器负责的就是“把这些 token 组织成层次结构”。在 KRL 程序中，读者关心的并不是单个 <code>DEF</code> 或 <code>IF</code> 本身，而是 <code>DEF ... END</code> 之间构成了一个过程定义，某个 <code>SWITCH ... ENDSWITCH</code> 构成了一个分支结构，某个 <code>targetName(...)</code> 则可能表示一次调用表达式。</p><p>因此，语法分析器的目标并不是记录“源码里出现过哪些词”，而是回答“这些词按什么结构组合在一起”。这一步完成后，程序文本就从“行和字符的集合”变成了“有层次的语言结构”，也就具备了后续静态分析的基础。</p><h3 id="82--start--dat--src">8.2 从入口规则开始：为什么 <code>start</code> 同时覆盖 <code>.dat</code> 与 <code>.src</code></h3><p><code>krl.g4</code> 的入口规则如下：</p><pre class="language-antlr lang-antlr"><code class="language-antlr lang-antlr">start
    : NEWLINE* krlControlHead moduleData EOF   # dataFile
    | NEWLINE* krlControlHead moduleSource EOF # sourceFile
    ;
</code></pre>
<p>这条规则非常关键，因为它首先回答了“这个文法究竟在解析什么文件”。在 KRL 中，数据文件和代码文件是两种不同的文本形态，但它们又共享一些共同成分，例如控制行和换行。因此，入口规则采用双分支建模：一条分支面向 <code>.dat</code> 文件，另一条分支面向 <code>.src</code> 文件。</p><p>这种写法有三个好处。第一，读者能够清晰看到文法覆盖的文件边界。第二，控制头被统一放在入口前部，不必在两个分支中重复书写。第三，后续所有规则都可以围绕“数据文件”和“代码文件”两类内容分别组织，而不至于混成一个巨大的不透明入口。</p><p>在阅读整个 <code>krl.g4</code> 时，入口规则就是最先要看的部分。它告诉读者：这不是一份只处理单一脚本形式的文法，而是一份同时覆盖两种文件类型的完整语言入口。</p><h3 id="83-">8.3 控制头与数据文件部分：语法层如何承接词法层的特殊内容</h3><p>控制头通过 <code>krlControlHead</code> 建模：</p><pre class="language-antlr lang-antlr"><code class="language-antlr lang-antlr">krlControlHead
    : (KrlControlLine NEWLINE+)*
    ;
</code></pre>
<p>这说明：文件头可以有零个或多个控制行，每个控制行后面跟一个或多个换行。这种写法延续了词法层对 <code>KrlControlLine</code> 的单独建模，也说明控制头虽然不属于普通程序体，但仍然是完整文件结构的一部分。</p><p>数据文件部分则以 <code>moduleData</code> 为核心：</p><pre class="language-antlr lang-antlr"><code class="language-antlr lang-antlr">moduleData
    : DEFDAT moduleName PUBLIC? NEWLINE+ dataList ENDDAT NEWLINE*
    ;
</code></pre>
<p>读者从这条规则中可以直接看到 KRL 数据文件的整体轮廓：先有 <code>DEFDAT</code>，后跟模块名，可选 <code>PUBLIC</code>，然后是若干数据行，最后以 <code>ENDDAT</code> 结束。这种写法把数据文件当作一个完整语法单元，而不是若干声明的散乱集合。</p><p>进一步往下看，<code>dataList</code> 与 <code>dataLine</code> 将枚举定义、结构体定义、变量声明、赋值表达式、旧式导入语句等内容统一纳入数据文件内部。这种组织方式说明，数据文件虽然主要服务于数据定义，但它内部仍然包含多种子结构，因此需要继续分层处理。</p><h3 id="84--modulesource-">8.4 代码文件部分：为什么 <code>moduleSource</code> 要区分主程序与子程序</h3><p>在 <code>.src</code> 文件部分，<code>moduleSource</code> 规则如下：</p><pre class="language-antlr lang-antlr"><code class="language-antlr lang-antlr">moduleSource
    : mainRoutine NEWLINE? (subRoutine | NEWLINE)*
    ;
</code></pre>
<p>这表明，一个 KRL 代码文件并不是简单的一串语句，而是由“主程序”以及若干“子程序”组成。这里的 <code>mainRoutine</code> 与 <code>subRoutine</code> 最终都可以落到过程或函数上，但把它们在模块层面区分开，有助于表达 KRL 文件的真实组织方式：文件通常有一个核心入口，其后还可能附带多个辅助过程或函数。</p><p>这一层设计的重要意义在于，它把“文件”与“程序单元”区分开了。文件是物理容器，过程和函数是逻辑单元。只有先在语法层把这两层分开，后续才能清晰理解：一个模块内部究竟定义了哪些可调用单元。</p><h3 id="85-">8.5 过程与函数：为什么要分开建模</h3><p>KRL 中的过程和函数虽然都属于可调用单元，但二者并不完全等价。过程通常对应无返回值的子程序，函数则带有返回类型，因此在文法中分别写成：</p><pre class="language-antlr lang-antlr"><code class="language-antlr lang-antlr">procedureDefinition
    : GLOBAL? DEF procedureName &#x27;(&#x27; parameterList &#x27;)&#x27; NEWLINE routineBody END
    ;

functionDefinition
    : GLOBAL? DEFFCT typeName functionName &#x27;(&#x27; parameterList &#x27;)&#x27; NEWLINE routineBody ENDFCT
    ;
</code></pre>
<p>这种区分看似细节，实际上非常关键。它不仅体现了 KRL 语言的原生分类，也避免在语法层把两种结构混成一个含糊规则。对于 ANTLR4 而言，这种明确分流可以降低歧义；对于读者而言，这也让 <code>krl.g4</code> 的结构更清楚：看到 <code>DEF</code> 与 <code>END</code> 就想到过程，看到 <code>DEFFCT</code> 与 <code>ENDFCT</code> 就想到函数。</p><p>同时，两种定义规则都把参数列表和 <code>routineBody</code> 纳入统一框架。这说明：虽然过程和函数在入口和结束关键字上不同，但它们内部结构仍然高度相似，因此可以共享后续的程序体规则。</p><h3 id="86-">8.6 程序体：声明区与实现区的划分</h3><p><code>routineBody</code> 在文法中被继续拆成两个部分：</p><pre class="language-antlr lang-antlr"><code class="language-antlr lang-antlr">routineBody
    : routineDataSection routineImplementationSection
    ;
</code></pre>
<p>这条规则反映了 KRL 程序组织的一个重要特点：程序内部通常既有声明区，也有执行语句区。声明区可能包含前置声明、变量定义、枚举定义、结构体定义等内容；实现区则由实际语句组成。</p><p>将二者分开，有助于解决两个问题。第一，读者可以清晰理解程序体内部结构，而不把“声明”和“执行”混在一起。第二，后续静态分析在寻找调用语句时，也能更明确地把注意力放在实现区，而不是对整个程序体一视同仁。</p><h3 id="87-statement">8.7 <code>statement</code>：为什么它是语法层最重要的规则之一</h3><p>在 KRL 中，真正决定程序运行流程的核心结构，大多汇聚在 <code>statement</code> 规则中。<code>krl.g4</code> 将空语句、<code>IF</code>、<code>FOR</code>、<code>LOOP</code>、<code>REPEAT</code>、<code>WHILE</code>、<code>SWITCH</code>、<code>WAIT</code>、<code>RETURN</code>、运动语句、触发语句、中断语句以及“表达式作为语句”等内容统一纳入 <code>statement</code>：</p><pre class="language-antlr lang-antlr"><code class="language-antlr lang-antlr">statement
    : NEWLINE
    | CONTINUE NEWLINE
    | EXIT NEWLINE
    | IF expression THEN NEWLINE statementList ...
    | FOR krlIdentifier &#x27;=&#x27; expression TO expression ...
    | LOOP NEWLINE statementList ENDLOOP NEWLINE
    | SWITCH expression NEWLINE+ switchBlockStatementGroups ENDSWITCH NEWLINE
    | expression NEWLINE
    | ...
</code></pre>
<p>这条规则之所以重要，是因为它承接了 KRL 程序中几乎所有“能发生在执行区里的语言行为”。把这些写法统一归入 <code>statement</code>，可以让语法树的结构保持一致：无论是分支、循环还是运动指令，本质上都是“程序体中的一条语句”。</p><p>同时，这种设计也适合 ANTLR4 的分层解析思路。语法分析器不需要一开始就判断某段文本是否具有业务意义，而只需先判断：它在语言结构上属于哪类语句。业务含义可以留到后续层面处理。</p><p>特别值得注意的是 <code>SWITCH</code>、<code>CASE</code>、<code>LOOP</code> 等结构。在工业机器人程序中，这些结构往往承担程序号分派、车型分支选择、工艺流程组织等重要职责，因此它们不仅是一般语法结构，也是后续静态分析时最值得关注的结构性节点。</p><h3 id="88--expression-">8.8 表达式系统：为什么 <code>expression</code> 要分层递归</h3><p>表达式规则是 <code>krl.g4</code> 中另一个核心部分。其典型形式如下：</p><pre class="language-antlr lang-antlr"><code class="language-antlr lang-antlr">expression
    : primary
    | op=(NOT | B_NOT) expression
    | op=(&#x27;+&#x27; | &#x27;-&#x27;) expression
    | expression op=&#x27;:&#x27; expression
    | expression op=(&#x27;*&#x27; | &#x27;/&#x27;) expression
    | expression op=(&#x27;+&#x27; | &#x27;-&#x27;) expression
    | expression op=(AND | B_AND) expression
    | expression op=(OR | B_OR) expression
    | expression op=(&#x27;==&#x27; | &#x27;&lt;&gt;&#x27; | &#x27;&lt;=&#x27; | &#x27;&gt;=&#x27; | &#x27;&lt;&#x27; | &#x27;&gt;&#x27;) expression
    | expression op=&#x27;=&#x27; expression
    ;
</code></pre>
<p>这条规则的目的，不只是“允许出现表达式”，更重要的是在文法中明确不同运算的层次关系。KRL 中既有普通算术运算，也有逻辑运算、关系运算、赋值运算，甚至还有用于几何变换的 <code>:</code> 运算。如果不把这些内容纳入统一表达式系统，许多语句就无法被稳定描述。</p><p>之所以采用分层递归写法，是为了把运算优先级和表达式组合方式显式化。对于读者来说，理解这条规则的关键，不是记住每个分支，而是明白它表达了一个思想：<strong>复杂表达式并不是平铺展开的，而是从最基础的 <code>primary</code> 开始，一层层向上扩展。</strong></p><p>这也是为什么表达式规则通常是阅读文法时最需要耐心的部分。它看起来比程序定义更抽象，但正因为如此，它也是语法分析器能够正确处理赋值、判断、调用和运动参数的基础。</p><h3 id="89-primary">8.9 <code>primary</code>：为什么变量、成员访问与函数调用必须分开</h3><p><code>primary</code> 规则定义了表达式体系中最基础的单位：</p><pre class="language-antlr lang-antlr"><code class="language-antlr lang-antlr">primary
    : &#x27;(&#x27; expression &#x27;)&#x27;
    | literal
    | variableName
    | variableName (&#x27;.&#x27; variableName)+
    | callableName=variableName &#x27;(&#x27; ... &#x27;)&#x27;
    ;
</code></pre>
<p>从这条规则可以看出，KRL 中最基础的表达式至少包括五类：括号表达式、字面量、普通变量、结构体成员访问以及调用表达式。它们在源程序文本中看起来都很常见，但在语法角色上并不相同。</p><p>例如，<code>varName</code> 只是一个变量引用，<code>pos.X</code> 则表示结构体成员访问，而 <code>ProgramName()</code> 则已经进入调用语义。若不在语法层把这些结构区分开，后续分析就很难稳定判断：某一段文本究竟是在取值、在访问成员，还是在发起调用。</p><p>因此，<code>primary</code> 的重要性在于，它把“看起来相似的基本表达式”分成了不同种类。这种拆分既有利于降低歧义，也为后续静态分析保留了更准确的结构线索。</p><h3 id="810-">8.10 字面量、结构体与枚举：为什么补充规则不能忽略</h3><p>除了程序定义、语句和表达式之外，<code>krl.g4</code> 还补充了 <code>literal</code>、<code>structLiteral</code>、<code>enumLiteral</code>、<code>typeName</code>、<code>primitiveType</code>、<code>userType</code> 等规则。它们看似不像 <code>statement</code> 那样显眼，但实际上共同决定了文法是否完整。</p><p>例如，KRL 中结构体字面量经常用于位置点、姿态点或工艺参数描述；枚举值则用 <code>#VALUE</code> 的形式表示离散语义。如果文法忽略这些内容，那么很多工业现场中常见的合法写法都会被误判为非法。换句话说，这些补充规则并不是边角料，而是在保证文法可用性。</p><h3 id="811-">8.11 关键规则与语言结构对照</h3><p>为了帮助读者建立整体认识，表 4 将若干关键规则与其对应的 KRL 语言结构做了整理。</p><table><thead><tr><th> 文法规则              </th><th> 对应语言结构 </th><th> 设计目的                           </th></tr></thead><tbody><tr><td> <code>start</code>               </td><td> 文件入口     </td><td> 同时兼容 <code>.src</code> 与 <code>.dat</code>          </td></tr><tr><td> <code>krlControlHead</code>      </td><td> 文件头控制行 </td><td> 单独承接元信息，不干扰程序体       </td></tr><tr><td> <code>moduleData</code>          </td><td> 数据文件主体 </td><td> 组织变量、枚举、结构体等内容       </td></tr><tr><td> <code>moduleSource</code>        </td><td> 代码文件主体 </td><td> 组织主程序与子程序                 </td></tr><tr><td> <code>procedureDefinition</code> </td><td> 过程定义     </td><td> 描述无返回值程序单元               </td></tr><tr><td> <code>functionDefinition</code>  </td><td> 函数定义     </td><td> 描述带返回值程序单元               </td></tr><tr><td> <code>statement</code>           </td><td> 执行语句     </td><td> 统一承接控制流、运动、调用等结构   </td></tr><tr><td> <code>expression</code>          </td><td> 表达式       </td><td> 组织优先级与运算结构               </td></tr><tr><td> <code>primary</code>             </td><td> 基础表达式   </td><td> 区分变量、成员访问、调用等基本形式 </td></tr></tbody></table><h3 id="812--krlg4">8.12 本章小结：如何整体阅读 <code>krl.g4</code></h3><p>如果读者想真正读懂 <code>krl.g4</code>，最有效的方式不是从中间随意挑一条规则看，而是按层次顺序阅读：</p><ol start="1"><li>先看 <code>start</code>，明确文件入口与整体分支；</li><li>再看 <code>moduleData</code> 与 <code>moduleSource</code>，理解文件级组织；</li><li>然后看过程、函数和程序体规则，理解程序单元结构；</li><li>接着看 <code>statement</code>，理解执行区主要语言现象；</li><li>再看 <code>expression</code> 与 <code>primary</code>，理解复杂语句内部如何组织；</li><li>最后回到 lexer 规则，理解这些结构所依赖的 token 基础。</li></ol><p><strong>图片标题：图 4 从 KRL 程序片段到语法树层次的示意图</strong></p><p><strong>图片内容描述：</strong> 图中以一个简化的 KRL 程序片段为例，展示其如何从“文件入口”逐步落到“程序定义—语句—表达式—基本表达式”的层次结构中，帮助读者建立阅读文法的顺序感。</p><h2 id="9-pojo">9. 从语法树到调用关系的理论转换路径规划(重点篇章，需结合项目中的pojo包里面相关代码进行举例展示)</h2><ul><li>说明语法分析结果不能直接等同于调用图，还需要进行语义层遍历与规则映射。</li><li>阐述如何从调用语句中提取被调程序名，并结合项目中的P程序、车型编号、车型程序、轨迹程序的语义进行层级映射</li><li>设计出对应的数据结构，指出数据的流动汇总过程。</li><li>解释为什么在工业机器人业务场景下，“语法识别 + 规则约束”的联合方法比单纯语法分析更可靠。</li></ul><p>需要说明的是，语法树并不直接等于调用关系图。词法分析与语法分析的任务，是把 KRL 程序还原为结构化的语言形式，例如“这里是一个 <code>SWITCH</code> 语句”“这里是一次调用表达式”“这里是一个过程定义”。而调用关系识别则还要继续回答：“这次调用在业务上属于哪一层程序关系”“某个分支标签在当前项目中代表什么含义”。</p><p>因此，从语法树到调用关系的转换，本质上是一条“语法识别 + 领域规则映射”的路径。前者负责保证文本被正确理解，后者负责把这些语言结构转化为工业现场真正关心的层级关系。没有语法基础，后续规则映射会变成脆弱的文本猜测；但仅有语法树，又不足以完全解释工业项目中的业务角色。</p><p>从理论上看，调用关系提取通常至少包括三个步骤。第一，识别可调用单元与调用表达式；第二，识别分支结构、程序号或标签等中间语义节点；第三，结合项目命名规范与结构约定，把这些语言结构组织成业务可理解的层级关系。由此可见，语法分析是后续工作的前提，但并不是全部。</p><h2 id="10-">10. 自动化调用关系识别方法的正确性与局限性</h2><ul><li>论证该方法在结构化 KRL 程序上的适用性与正确性来源。</li><li>说明其对规范命名、固定项目组织结构和配置辅助规则的依赖。</li><li>分析其局限性，如动态行为、间接语义、项目非标准写法等难以完全通过静态分析覆盖。</li></ul><p>本文方法的正确性，主要来自两个方面。其一，KRL 本身具有较强的形式结构，关键字边界、程序定义、控制流与调用表达式都可以通过文法规则稳定识别。其二，工业现场的 KRL 项目往往遵循一定模板和命名规范，使得语法分析结果能够进一步被组织成更有意义的程序结构。</p><p>但这并不意味着自动化分析可以无条件覆盖所有情况。首先，静态分析无法完全还原运行时动态行为。例如某些条件分支是否真正执行，某些参数如何影响调用路径，并不能仅靠语法树完全判断。其次，若项目中存在大量非规范命名、历史遗留写法、极少见的控制器版本差异，文法与规则就可能需要额外适配。再次，工业程序中有些语义并不直接写在语法结构里，而是隐含于工艺约定或设备配置中，这超出了纯语法分析的能力边界。</p><p>因此，更准确的结论是：基于 KRL 文法和编译原理的自动化分析方法，对于结构化、规范化的工业机器人程序具有较高适用性，是构建程序理解系统的合理基础；但在面对复杂现场时，仍应保留对领域规则、项目约定和人工复核的依赖。</p><h2 id="11-">11. 结论</h2><ul><li>总结本文对“自动识别工业机器人程序&quot;在kuka语言、编译理论方面的分析论证结果。</li><li>强调 ANTLR4 词法与语法分析在本项目中的核心基础作用。</li><li>为后续工程实现报告做好理论承接。</li></ul><p>本文围绕 KRL 语言和编译原理方法，对工业机器人程序自动识别问题进行了理论层面的分析与论证。结论可以概括为三点。第一，KRL 程序虽然服务于工业控制场景，但其文本形式具有清晰的结构边界，因此适合采用词法分析与语法分析方法进行静态处理。第二，自动化识别程序结构在数据来源、工程成本和工具链成熟度方面都具备现实可行性。第三，通过对 KRL 主要语法结构进行整理，并按照 ANTLR4 <code>LL(*)</code> 的要求进行文法建模，可以形成一套兼具可读性、可维护性和可扩展性的 <code>krl.g4</code> 规则体系。</p><p>从全文结构来看，第 7 章与第 8 章构成了本文的核心：前者回答“字符如何被切成有意义的词法单元”，后者回答“这些词法单元如何组成层次化语法结构”。当这两部分建立起来以后，KRL 程序就不再只是难以直接阅读的工业文本，而成为可以被系统解释的形式语言对象。</p><p>因此，<code>krl.g4</code> 不只是一个工程文件，更是本文理论分析的集中体现。它把 KRL 语言从现场经验层面的“会看程序”，转化为可被形式化描述、可被自动分析处理的对象。后续工程实践报告将在此基础上继续展开，说明这些文法规则如何支撑模块识别、调用关系提取、可视化展示与 Excel 导出等实际功能。</p><h2 id="">参考资料</h2><ol start="1"><li>KUKA Robot Language（KRL）相关语法资料与控制器编程说明。</li><li>ANTLR4 官方文法设计与 <code>LL(*)</code> 分析机制相关文档。</li><li>项目文法文件：<code>/Users/liuke/IdeaProjects/KRLParser/krl-core/src/main/resources/krl.g4</code>。</li></ol></div><p style="text-align:right"><a href="https://waitforu.tech/posts/develope/krl#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://waitforu.tech/posts/develope/krl</link><guid isPermaLink="true">https://waitforu.tech/posts/develope/krl</guid><dc:creator><![CDATA[Cody]]></dc:creator><pubDate>Mon, 09 Mar 2026 23:12:38 GMT</pubDate></item><item><title><![CDATA[Git的工作流]]></title><description><![CDATA[<link rel="preload" as="image" href="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F24%2F164128-b71bf3de79de73c994ff81fc37c5f95f.webp"/><link rel="preload" as="image" href="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F111809-478b4459c040795a9e85940498cc9fe7.webp"/><link rel="preload" as="image" href="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F111910-81740f439814ae18f424e26223a79f93.webp"/><link rel="preload" as="image" href="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F111922-d3e33de3200bc256cb8a4de979bba2ae.webp"/><link rel="preload" as="image" href="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F111939-0e865b99ed82f6149577323b3016b59a.webp"/><link rel="preload" as="image" href="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F120301-9d3f7bbeb1f9ed6efc79348ca81b7936.webp"/><link rel="preload" as="image" href="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F111947-2d9ae5fa84f2579ccfd2fa06cba928c5.webp"/><link rel="preload" as="image" href="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112011-1a778d061daf7a536b8482f962d3e10c.webp"/><link rel="preload" as="image" href="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112034-d601340d9cde31d644b992fa3d4a4a4c.webp"/><link rel="preload" as="image" href="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112058-9ee3042071719bf699825e0180cec83a.webp"/><link rel="preload" as="image" href="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112105-acfc416dda63747457ca9a60dac9d023.webp"/><link rel="preload" as="image" href="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112111-9c4ed5617284e5c391ec4927b2d9509f.webp"/><link rel="preload" as="image" href="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112115-df18e493e5d4eef95e5bb122b2e4f9d1.webp"/><link rel="preload" as="image" href="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112119-a57faaf501f0f167c0c1737d568b428f.webp"/><link rel="preload" as="image" href="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112123-33d3cbf65b539339d74cf826cb97faac.webp"/><link rel="preload" as="image" href="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112127-4f618531766f49f02b271964e01a5ea8.webp"/><div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://waitforu.tech/posts/develope/git-workflow">https://waitforu.tech/posts/develope/git-workflow</a></blockquote><div><h1 id="">本文参考链接</h1><ul><li><p>清华杨希杰的git分享
[https://thu-ios.github.io/tutorials/lecture/git.html]</p></li><li><p>up主「玄离199」的git视频教程
[https://www.bilibili.com/video/BV1d6XVYqEuy?spm<em>id</em>from=333.788.player.player<em>end</em>recommend<em>autoplay&amp;vd</em>source=e5f4d0f4141c018524aa67bc05a7a2c2]</p></li><li><p>Python core dev成员高天大佬的工作流视频</p><ul><li><p>[https://www.bilibili.com/video/BV19e4y1q7JJ?spm<em>id</em>from=333.788.recommend<em>more</em>video.1&amp;vd_source=e5f4d0f4141c018524aa67bc05a7a2c2]</p></li><li><p>[https://www.bilibili.com/video/BV1ne4y1S7S9?spm<em>id</em>from=333.788.recommend<em>more</em>video.-1&amp;vd_source=e5f4d0f4141c018524aa67bc05a7a2c2]</p></li></ul></li><li><p>Git 飞行规则指南
[https://github.com/k88hudson/git-flight-rules/blob/master/README_zh-CN.md]</p></li></ul><h1 id="">配置设定</h1><h2 id="">配置</h2><p><strong>本机上的git信息:姓名、邮箱</strong></p><pre class="language-shell lang-shell"><code class="language-shell lang-shell">git config user.name    # Cody

git config user.email   # liuke504356312@gmai.com
</code></pre>
<p>使用下列命令进行更改</p><pre class="language-shell lang-shell"><code class="language-shell lang-shell">git config --global user.name Cody   #更改名称
git config --global user.email liuke504356312@gmai.com   #更改邮箱
</code></pre>
<p>在与远程仓库进行 代码交互时，本地的分支应该与远程的一个分支相对应(相绑定)，否则在第一次push时会报错。但是我们也可以设置Git自动为新分支设置上游·</p><pre class="language-shell lang-shell"><code class="language-shell lang-shell"># 设置Git 会自动绑定 dev 到远程同名分支。
git config --global push.autoSetupRemote true
</code></pre>
<p><strong>gitignore(指明不参与git上传的文件信息，比如密码文件、图片文件等)</strong></p><p>在当前目录上创建<code>.gitignore</code>文件。其中填写的文件与文件夹不会参与git上传</p><blockquote><p>想更精细可去 <a href="https://www.toptal.com/developers/gitignore">https://www.toptal.com/developers/gitignore</a> 自动生成。例如java, intellij, maven, gradle, macos 的组合模板。</p></blockquote>
<img src="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F24%2F164128-b71bf3de79de73c994ff81fc37c5f95f.webp" alt="image-20250724164128210"/><p><strong>macbookpro的git本机密钥</strong></p><pre class="language-shell lang-shell"><code class="language-shell lang-shell">#存储位置
/Users/yourname/.ssh/id_ed25519            # identificatiopn (private key)
/Users/yourname/.ssh/id_ed25519.pub  # public key

#密码，用来保护私钥，未进行设置。否则每次ssh连接github还需使用密码提取私钥才能进行
</code></pre>
<p><img src="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F111809-478b4459c040795a9e85940498cc9fe7.webp" alt="image-20250524180128832" height="1424" width="1998"/></p><h2 id="">分区</h2><p>  工作区（Working Directory）←→ 暂存区（Stage/Index）←→ HEAD（最后一次 commit）</p><ul><li>工作区：你正在编辑的真实文件；</li><li>暂存区：用 git add 添加后的内容，准备提交；</li><li>HEAD：你最后一次 git commit 的版本快照。</li></ul><h1 id="">命令介绍</h1><p><img src="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F111910-81740f439814ae18f424e26223a79f93.webp" alt="git的工作示意图"/></p><h2 id="git-diff">git diff</h2><pre class="language-shell lang-shell"><code class="language-shell lang-shell">git diff # worktree &amp; stage (Changes in the working tree not yet staged for the next commit.) 改了但还没 add 的内容

git diff HEAD # worktree &amp; HEAD (Changes in the working tree since your last commit.)  add 了、还没 commit 的内容

git diff --cached # stage &amp; HEAD (Changes between the index and your last commit.)     总体来看，哪些东西跟上次提交不一样（包含 add 和没 add 的）

git diff &lt;commit&gt; &lt;commit&gt; # between two commits   看某两次提交之间的变更

#可以加 --stat 查看概览（文件改了多少行）：
git diff --cached --stat
#可以加 --name-only 只显示变动文件名：
git diff HEAD --name-only
</code></pre>
<h2 id="git-restore">git restore</h2><p>舍弃更改 等价于 用某些之前的东西来覆盖</p><pre class="language-sh lang-sh"><code class="language-sh lang-sh">git --help restore # 查看restore命令的帮助文档
</code></pre>
<p><code>git restore</code> == <code>git restore --worktree</code> 默认是覆盖worktree</p><pre class="language-shell lang-shell"><code class="language-shell lang-shell"># 参数简介

#指定 覆盖文件来自的分区 。
#默认情况下，覆盖工作区文件，来自分区为 暂存区(Stage/Index)，覆盖暂存区文件，来自分区为HEAD区
-s &lt;tree&gt;, --source=&lt;tree&gt;
Restore the working tree files with the content from the given tree. If not specified, the default restore source for the working tree is the index, and the default restore source for the index is HEAD.


#指定 要被覆盖文件的分区，默认情况为工作区(worktree)
-W, --worktree, -S, --staged
Specify the restore location. If neither option is specified, by default the working tree is restored.
</code></pre>
<h2 id="mergerebase">merge与rebase辨析</h2><table><thead><tr><th> <strong>操作</strong>   </th><th> <strong>目的</strong>                   </th><th> <strong>原理</strong>               </th><th> <strong>提交记录</strong>               </th></tr></thead><tbody><tr><td> git merge  </td><td> 合并两条提交线             </td><td> 自动创建一个“合并提交” </td><td> 会出现一个“合并 commit”    </td></tr><tr><td> git rebase </td><td> 把你的提交“搬到”最新主干上 </td><td> 重新排列提交           </td><td> 保持提交线清晰，没有合并点 </td></tr></tbody></table><p>对于非公共分支，推荐使用rebase：</p><table><thead><tr><th> <strong>优点</strong>           </th><th> <strong>说明</strong>                                                     </th></tr></thead><tbody><tr><td> ✅ 更干净的提交历史 </td><td> 没有一堆“Merge branch xxx”的噪音提交                         </td></tr><tr><td> ✅ 提交线性         </td><td> 每个人的提交看起来都是从当前最新的主干出发                   </td></tr><tr><td> ✅ 更容易 review    </td><td> Pull Request中每个commit都是干净的功能提交,没有因主干改变而产生&quot;追随主干的额外提交&quot; </td></tr></tbody></table><p>✅ 推荐使用rebase的时机</p><p>即，先rebase，在push。 对已经上传的不要再进行rebase变基了。</p><table><thead><tr><th> <strong>何时使用</strong>                           </th><th> <strong>推荐</strong>                             </th></tr></thead><tbody><tr><td> 合并主干代码前                         </td><td> ✅ 使用 git rebase main               </td></tr><tr><td> 已经 push 到远程、团队成员也基于你工作 </td><td> ⚠️❌ 不推荐 rebase（会让别人历史错乱） </td></tr></tbody></table><blockquote><p>rebase 就像“排练”，让你提交历史整洁、看起来像没分叉过；即，你的新分支修改就是 基于最新的 主干main分支。</p></blockquote><blockquote>
<p>merge 就像“记录真实开发流程”，保留所有分叉和合并；即，如果修改分支是基于 上一版本的 主干main，当前新版的main分支有冲突，解决冲突的过程也会当作一个commit会被记录。</p></blockquote>
<h2 id="branch">分支(branch)</h2><p>创建一个分支，你可以在这个分支上开发新功能A，过一段时间，你又想开发新功能B，这时你可以再开一个分支。过了一段时间，你两个新功能都开发好了，就可以将两个分支合在一起，这样你的产品就同时有了两个新的功能。</p><pre class="language-shell lang-shell"><code class="language-shell lang-shell">git branch --create(-c) &lt;newBranch&gt; # 创建分支
git branch # 查看本地分支
git switch &lt;newBranch&gt; # 切换分支
</code></pre>
<pre class="language-shell lang-shell"><code class="language-shell lang-shell"># 开发好了新功能之后
git switch main # 切换到主分支，因为我们的所有功能都要合并到一个分支上
git merge &lt;newBranch&gt; # 将开发新功能的分支的内容合并（merge）到main分支上
git branch --delete(-d) &lt;newBranch&gt; # 删除开发新功能的分支，因为开发好了合并进来原来的开发分支就没有用了
</code></pre>
<pre class="language-shell lang-shell"><code class="language-shell lang-shell"># 需要在不提交当前未完成工作的情况下，临时切换分支或进行紧急修复时，可以使用git stash
# 比如你突然发现 main 分支有一个 Bug 需要立即修复，但你的新功能还没写完，不想提交一个“未完成”的 Commit。
git stash #工作区瞬间变得干净，你可以安全地切换到 main 分支进行 Bug 修复
#修复完成后，切换回你的功能分支，执行
git stash pop #未完成的代码会重新出现在你的工作区，你可以继续工作。
</code></pre>
<p>设置默认分支从<code>master</code>变为<code>main</code></p><pre class="language-shell lang-shell"><code class="language-shell lang-shell"># 查看Git版本
git --version
# Since git version 2.28.0

git config --global init.defaultBranch main
# 之后使用git init初始化项目的默认分支会变为main
git init

# or for one repo
# 还未创建的项目
git init -b main
# 已经存在的项目
git branch --move(-m) master main 
</code></pre>
<h2 id="">版本回退</h2><p>版本回退可以通过<code>git reset</code> 与<code> git revert</code>这两条指令进行</p><p>首先我们需要明确两点：每一次<code>commit</code>的编号，一方面代表所有文件的状态，另一方面代表这一次<code>commit</code>相比于上一次<code>commit</code>所做的更改。</p><p>那么我们如果希望回到之前的某个<code>commit</code>，指的就是直接将工作区所有文件都恢复成那个<code>commit</code>之后的样子。</p><p>这时我们需要使用：</p><pre class="language-sh lang-sh"><code class="language-sh lang-sh">git reset --hard &lt;commit&gt;
</code></pre>
<p>如果我们希望撤销某次<code>commit</code>所做出的更改，那么我们需要使用：</p><pre class="language-sh lang-sh"><code class="language-sh lang-sh">git revert &lt;commit&gt; # Git帮你提交
git revert -n &lt;commit&gt; # -n: --no-commit，自己提交
</code></pre>
<p><img src="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F111922-d3e33de3200bc256cb8a4de979bba2ae.webp" alt="image-20250524181847810"/></p><p>两种回退的分析：</p><p><code>git reset --hard</code>是非常方便的回退方式。如果你写代码提交了两个<code>commit</code>之后去吃饭，吃完回来一看，觉得刚刚状态不太好写的不行，于是你<code>git reset --hard</code>回到两个版本之前，发现所有的东西都变成了你修改代码前的样子。</p><p><code>git reset --hard</code>会删除<code>commit</code>，所以如果你在某次<code>commit</code>中添加了敏感信息（如账号密码等），那么使用<code>git reset --hard</code>进行恢复是十分明智的选择。</p><p>但是要注意，在上面的案例中，如果你已经将两次<code>commit</code>使用<code>git push</code>上传到了Git服务器，那么<code>git reset --hard</code>一定会给团队协作带来混乱（因为你直接删除了两个commit！）。这时怎么办呢？你可以使用两次<code>git revert -n</code>再<code>git commit</code>，这样你会新生成一个<code>commit</code>，这个<code>commit</code>的内容和往前两次<code>commit</code>的内容一样，由于这是新生成的<code>commit</code>，所以push到服务器也完全没有问题。</p><p>那么如果把密码<code>git push</code>到服务器了呢？这时<code>Git</code>已经帮不了你了。要不直接在代码托管平台直接删掉项目吧（不推荐），要不就赶紧去改密码吧…</p><p>注：其中的版本号<code>&lt;commit&gt;</code>可以通过<code>git log</code>查看。</p><p>注：向后回退一个版本可以写为<code>git reset --hard HEAD^</code>，<code>HEAD^^</code>表示当前版本向后2个<code>commit</code>，<code>HEAD~n</code>表示当前版本向后n个<code>commit</code>。</p><h2 id="">远程仓库相关</h2><ul><li>绑定远程仓库</li></ul><pre class="language-shell lang-shell"><code class="language-shell lang-shell"># 本地文件夹与远程仓库绑定 my-repo-nickname远程仓库别名， my-repo-url远程仓库地址 
git remote add &lt;my-repo-nickname&gt; &lt;my-repo-url&gt;
</code></pre>
<pre class="language-shell lang-shell"><code class="language-shell lang-shell"># 示例: 绑定后直接用testRepository就可以代指github上的仓库
git remote add testRepository git@github.com:liukegeek/FanucAgent.git
</code></pre>
<p>查看关联的远程仓库</p><pre class="language-shell lang-shell"><code class="language-shell lang-shell">git remote --verbose
git remote --verbose show &lt;repo-nickname&gt;
</code></pre>
<p><img src="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F111939-0e865b99ed82f6149577323b3016b59a.webp" alt="image-20250527155219064"/></p><ul><li>克隆远程仓库</li></ul><pre class="language-shell lang-shell"><code class="language-shell lang-shell">git clone &lt;team-repo-url&gt; --origin &lt;team-repo-nickname&gt; &lt;project-folder&gt;
# 通过clone ，可以下载远程仓库的文件，并自动完成本地仓库与远程仓库的绑定。
</code></pre>
<p><code>--origin &lt;远程仓库名&gt;</code>用来给默认的名字origin重命名</p><p><code>&lt;project-folder&gt;</code>指本地新出现的文件夹的名称</p><pre class="language-shell lang-shell"><code class="language-shell lang-shell">#示例：
git clone git@github.com:liukegeek/FanucAgent.git --origin FancuDemo demoFolder
git clone git@github.com:liukegeek/FanucAgent.git 
#后面参数可省略，这样默认远程分支名为orgin，下载的文件夹为 远程仓库的项目名称
</code></pre>
<ul><li>拉取远程仓库的内容</li></ul><pre class="language-shell lang-shell"><code class="language-shell lang-shell"># 拉取最新的内容
git fetch &lt;team-repo-nickname&gt; # 或 git fetch --all
</code></pre>
<pre class="language-shell lang-shell"><code class="language-shell lang-shell">git switch main  #将`远程仓库某分支`的内容 与`本地仓库分支` 合并，保持同步
git merge &lt;team-repo-nickname&gt;/main
</code></pre>
<p>然后自己新开一个修改分支，完成自己的修改后用<code>git merge &lt;newBranch&gt;</code> 将修改分支合并到主分支。</p><p>主分支、修改分支合并时，要先重新fetch，确保主分支与远程分支同步后再合并。</p><p>如果主分支、修改分支间有冲突，则解决冲突后还要记得再fetch，确保新的主分支与远程分支间没有冲突。</p><p>总之，要保证push时，不会与远程仓库发生冲突。</p><ul><li>push到自己的远端仓库</li></ul><pre class="language-shell lang-shell"><code class="language-shell lang-shell"># 将某一本地分支推到远程主机的远程分支；如果远程仓库没有这一分支，则创建
git push &lt;my-repo-nickname&gt; newFeature:newFeature
# git push &lt;远程主机名&gt; &lt;本地分支名&gt;:&lt;远程分支名&gt;             前面的是本地分支名，后面的是远程分支名，

#如果远程、本地分支名称一样，则可简写为:
git push &lt;my-repo-nickname&gt; newFeature

#注意: gitHub默认为main，因此在往github提交代码时，最好把本地主分支改名为 main
</code></pre>
<p>示例</p><pre class="language-shell lang-shell"><code class="language-shell lang-shell">git push testRepository master:main
git push testRepository newFeature:newFeature
git push orgin localName:remoteName

git push testRepository main
git push testRepository newFeature:newFeature
</code></pre>
<p><img src="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F120301-9d3f7bbeb1f9ed6efc79348ca81b7936.webp" alt="image-20250525010221023"/></p><ul><li>删除远程分支
<ul><li>方法一：在团队仓库的PR界面，如果成功merge，那么你可以看到一个<code>delete branch</code>的按钮，可以直接点击按钮删除自己fork的仓库中的newFeature分支</li><li>方法二：<code>git push &lt;my-repo-nickname&gt; --delete newFeature</code></li></ul></li><li>删除本地分支
<ul><li>切换到主分支下，然后执行<code> git branch -d newFeature</code>指令</li><li>使用<code>git remote prune yxj</code>删掉本地陈旧的远端分支（就是 远端已经删除掉了但是本地还没删除掉的分支）</li></ul></li></ul><h2 id="">标签管理</h2><p>tag就是一个让人容易记住的有意义的名字，它跟某个commit绑在一起。见github主页的tag部分</p><p><img src="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F111947-2d9ae5fa84f2579ccfd2fa06cba928c5.webp" alt="image-20250527161347858"/></p><pre class="language-shell lang-shell"><code class="language-shell lang-shell"># 给当前commit的版本打上标签: first versioin
git tag -a &quot;1.0&quot; -m &quot;first versioin&quot;

#将标签上传至远程仓库: 在push命令后面添加 --tags 参数.
git push origin  --tags  
# 注意： 这个tags是直接带着·资源·发布的，而不是跟自己的远程分支绑定，即使添加tag与所在分支没有任何关系。
</code></pre>
<p>现在从<strong>实用场景</strong>角度，把 Git 标签（tag）为什么存在、什么时候该用  介绍一下：</p><hr/><p> 一、标签（Tag）到底是干什么用的？</p><p>标签是 Git 中用于“给某个提交打上里程碑”的功能。</p><p><strong>可以将其理解成：</strong>“给历史上的某一个提交贴个版本号的标签”。</p><p>📌 标签是：</p><blockquote><p>🏷️ 一个永久性的、只读的、不会随分支移动的名字。</p></blockquote>
<p> 二、哪些场景下必须用标签（tag）</p><table><thead><tr><th> <strong>场景</strong>                                              </th><th> <strong>是否需要 tag</strong> </th><th> <strong>原因</strong>                           </th></tr></thead><tbody><tr><td> 发布正式版本（如 v1.0.0）                             </td><td> ✅ 必须           </td><td> 用来标记当前代码为“稳定版本”       </td></tr><tr><td> 开发周期结束阶段，需要生成版本包（ZIP、release note） </td><td> ✅ 必须           </td><td> GitHub Release 等工具依赖 tag      </td></tr><tr><td> 自动化部署（CI/CD）                                   </td><td> ✅ 常用           </td><td> 一般会监听 tag 触发部署流程        </td></tr><tr><td> 回滚、定位某次发布                                    </td><td> ✅ 推荐           </td><td> tag 是找回某个 commit 的“标签索引” </td></tr></tbody></table><p>三、哪些场景下推荐/没必要使用 tag？</p><table><thead><tr><th> <strong>场景</strong>                             </th><th> <strong>是否需要 tag</strong> </th><th> <strong>原因</strong>                     </th></tr></thead><tbody><tr><td> 在个人开发分支中做临时提交           </td><td> ❌ 不需要         </td><td> commit 自己就能记录开发进展  </td></tr><tr><td> 功能开发中想做多个提交（未准备发布） </td><td> ❌ 不需要         </td><td> commit + push 就够了         </td></tr><tr><td> 团队协作过程中不断合并代码           </td><td> ❌ 不需要         </td><td> 分支足够表达逻辑，tag 会混乱 </td></tr></tbody></table><blockquote><p>✳️ <strong>自己开了一个功能分支，用来开发一个功能，我需要打 tag 吗？</strong></p></blockquote>
<p><strong>❌ 不需要！</strong></p><p>只需要按需提交即可（使用 commit）；等功能完成、合并到主干后，主干准备发布了，<strong>那时才需要 tag</strong>。</p><hr/><blockquote><p>✳️ <strong>是不是只有管理主干分支 main 的时候，才需要 tag？</strong></p></blockquote>
<p>​     ✅ 几乎可以这么说。</p><p>大部分项目的 tag 都是打在 main（或 master）分支的某个特定提交上，代表的是：</p><ul><li>v1.0.0 发布</li><li>v2.1.3 修复版</li><li>v3.0.0 重构版本</li></ul><hr/><p>🎯 小结口诀</p><blockquote><p>✳️<strong>功能开发看分支，版本发布看标签。</strong></p></blockquote><blockquote>
<p>分支记录过程，标签记录里程碑。</p></blockquote>
<hr/><p>✅ 最佳实践推荐</p><table><thead><tr><th> <strong>阶段</strong> </th><th> <strong>建议做法</strong>                                                 </th></tr></thead><tbody><tr><td> 功能开发 </td><td> git commit + git push，不要打 tag                            </td></tr><tr><td> 功能合并 </td><td> 合并到主干，开始准备发布                                     </td></tr><tr><td> 准备发布 </td><td> git tag -a v1.0 -m &quot;发布1.0版本&quot;，然后 git push origin --tags </td></tr><tr><td> 版本回顾 </td><td> 通过 git tag / git show v1.0 找到历史                        </td></tr></tbody></table><h2 id="">异常解决</h2><p><img src="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112011-1a778d061daf7a536b8482f962d3e10c.webp" alt="image-20250525002605970"/></p><p>错误原因是：</p><ul><li>GitHub 上的 main 分支，<strong>已经有一些提交</strong>；</li><li>而你本地的 main 是从零开始创建的，<strong>没有那些提交历史</strong>；</li><li>Git 拒绝直接推送，是为了防止你<strong>“覆盖别人已有的内容”</strong>。</li></ul><p>解决办法：</p><pre class="language-shell lang-shell"><code class="language-shell lang-shell">git pull --rebase testRepository main

# 解释：
#    •    git pull 是 先把远程仓库的提交拉取到你本地；
#    •    --rebase 是 把你的提交“叠加”在远程提交之上，这样避免了多余的“合并记录”；
#    •    testRepository 是你之前设置的远程仓库名。
</code></pre>
<pre class="language-shell lang-shell"><code class="language-shell lang-shell">#首次推送本地分支给远程仓库的流程
# 1. 确保绑定远程仓库分支
git remote add remoteName git@github.com:xxx.git

# 2. 拉取远程已有提交,并通过变基方式合并
git pull --rebase origin main

# 3. 正常推送
git push
</code></pre>
<p>当然，也可以拉取远程仓库的内容到本地，然后合并后再推送。这也是每次推送都应遵循的做法，包括第一次。</p><pre class="language-shell lang-shell"><code class="language-shell lang-shell">git fetch &lt;repo-nickname&gt; # 将远程仓库的内容拉取到本地
git merge # 将本地中远程仓库的内容merge到当前分支
</code></pre>
<h1 id="github">GitHub工作流</h1><blockquote><p>参考于<code>高天</code>的视频：<a href="https://www.bilibili.com/video/BV19e4y1q7JJ?spm_id_from=333.788.recommend_more_video.1&amp;vd_source=e5f4d0f4141c018524aa67bc05a7a2c2">github工作流</a></p></blockquote>
<ol start="1"><li>clone远程仓库</li></ol><pre class="language-shell lang-shell"><code class="language-shell lang-shell">git clone git@github.com:liukegeek/FanucAgent.git

#详细版:指明remote的仓库名为FanucDemo(否则默认为origin)，下载的文件夹为demoFolder(否则默认为项目名)
git clone git@github.com:liukegeek/FanucAgent.git --origin FanucDemo demoFolder
</code></pre>
<ol start="2"><li>本地创建分支</li></ol><pre class="language-shell lang-shell"><code class="language-shell lang-shell">#创建并切换分支， 新版git的命令，语义更清晰。
git branch -c myFeature
git switch myFeature
# 也可以直接一步到位，直接创建并切换分支
git switch -c myFeature

#老命令，等同于创建分支并切换过去，等价于git switch -c myFeature，建议优先使用新式命令
git checkout -b myFeature
</code></pre>
<ol start="3"><li>对work directory文件进行修改，并保存到本地仓库</li></ol><pre class="language-shell lang-shell"><code class="language-shell lang-shell">git diff         # 查看更改了哪些内容
git add .   # 添加所有文件
git commit -m &quot;message&quot; # 带注释信息提交
</code></pre>
<ol start="4"><li>拉取同步远程的最新主干</li></ol><p>看是否有别人新的update，导致remote main 与local main不一致，防止冲突.</p><p>可以通过pull自动进行合并，也可以fetch手动进行合并。</p><pre class="language-shell lang-shell"><code class="language-shell lang-shell"># 对于非公共分支，建议使用fetch + rebase进行合并
(main): git fetch origin main   #拉取远程main的数据，但不合并。不加main会拉取远程仓库的所有分支信息
(main): git merge origin/main   # 将拉取到的远程main的数据，合并到本地main中


git switch myFeature                    # 转换成修改分支myFeature
(myFeature): git rebase main        # 将 我的修改先mian的最新版本拿过来 然后再把我的修改尝试添加到新版main上。




# 拉取也可以使用pull，pull既拉取又合并
git pull origin main  #等效于： git fetch origin main    +     git merge origin/main
# 如果省略main，git pull origin 就变成根据当前所载分支来拉取对应的远程分支，并合并



#合并也可以接着用merge，这一步merge和上面rebase的目的都是，将修改分支的main更新成新版的main
git switch myFeature
(myFeature): git merge main  #将 主分支main 合并到修改分支myFeature上。
#merge会有合并提交的记录，rebase会让提交更线性干净。公共分支别用rebase，否则会改变其他成员的历史
</code></pre>
<ol start="5"><li>推送远程</li></ol><pre class="language-shell lang-shell"><code class="language-shell lang-shell">git push origin myFeature/myFeature  #若remote端无该分支，则将在github上面新增一个分支:myFeature,并将本地myFeature的信息同步进去。
#注意，推荐在 myFeature端 进行push，不要直接操作 master branch界面进行push ！！！ 否则如果remote 的main有新update，本地的master还需要考虑冲突与合并。
</code></pre>
<p><img src="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112034-d601340d9cde31d644b992fa3d4a4a4c.webp" alt="image-20250527234544817"/></p><p>注意，图片中<code>-f</code>是强制合并的意思。因为是先push，发现有冲突后在pull、rebase解决冲突。而rebase 会重写提交历史（提交的哈希值变了），导致本地的历史和 GitHub 上的远程历史不一样。而Git 为了保护远程历史，默认不允许 push 改写后的提交。因此必须使用<code>-f</code>参数进行强制推送。</p><p>显然如果是该分支首次push，此时remote还没有该分支，那么就不会有远程历史与本地历史不一致问题，推送也不会被拒绝，我们也就不需要加<code>-f</code>参数。</p><p>但是在本篇中，我们优化了流程，在提交并建立remote端时就已经在本地完成先进行了比对，有冲突先rebase再首次提交。 因此大概率第一次push就是基于最新版的master，直接推上去就结束了(不用加 -f）。除非是这个分支的非首次提交，且项目的主干又有了新的更新，我们被迫需要rebase到新主干上去。这时就必须加<code>-f</code>了，不过由于更改分支一般是自己创建仅用于个人修改，且已经在本地rebase了最新主干，因此强制push也不会有影响。</p><ol start="6"><li>发起PR(Pull Request)</li></ol><p>进入分支页面，点击commit内容，查看提交详情</p><p><img src="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112058-9ee3042071719bf699825e0180cec83a.webp" alt="image-20250528133008416"/></p><p>点击create pull request按钮，进入申请界面</p><p><img src="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112105-acfc416dda63747457ca9a60dac9d023.webp" alt="image-20250528132858272"/></p><p>填写相关描述，创建pr申请。</p><p><img src="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112111-9c4ed5617284e5c391ec4927b2d9509f.webp" alt="image-20250528133325517"/></p><ol start="6"><li>审查代码，合并提交</li></ol><p>有可能一个功能分支上会带着许多commit，我们不希望把功能分支上的所有branch都放到main branch上，我们希望我们的main branch history 尽可能简洁、正常工作。因此，大多数情况下，面对Pull Request 我们会选择 <strong>Squash and merge: 一个分支上的所有改变整合成一个改变,然后放入到main branch上</strong>。 这不会影响具体的代码改动，只是comimt history中的 commit的结构、数量、名字发生了改变。</p><ul><li>进入Pull requests界面，点击分支名称，进去详情界面查看更改。</li></ul><p><img src="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112115-df18e493e5d4eef95e5bb122b2e4f9d1.webp" alt="image-20250528122749643"/></p><ul><li>将功能分支通过<code>Squash and Merge</code>合并到主分支上</li></ul><p><img src="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112119-a57faaf501f0f167c0c1737d568b428f.webp" alt="image-20250528122642825"/></p><ul><li>为合并后生成的一个commit，添加信息说明。</li></ul><p><img src="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112123-33d3cbf65b539339d74cf826cb97faac.webp" alt="image-20250528133908136"/></p><ol start="6"><li>代码审查通过后，删除分支</li></ol><p>remote端的 myFeature 可以通过 gitHub上面的一个按钮删除。</p><p><img src="https://obj.waitforu.tech/imgs%2Fblog%2F2025%2F07%2F25%2F112127-4f618531766f49f02b271964e01a5ea8.webp" alt="image-20250528134231473"/></p><p>local 端的myFeature 通过 <code>-d</code>指令删除。</p><pre class="language-shell lang-shell"><code class="language-shell lang-shell">git switch main  # 切换回主分支
git pull origin main    #拉取remote端的分支，让local 与 remote 的main 重新保持同步。
git branch -d myFeature      #删除已经提交完不在使用的分支：myFeature
</code></pre>
<h1 id="">附：可能有用的命令整理</h1><pre class="language-shell lang-shell"><code class="language-shell lang-shell">
git add xyz                                                 # 添加xyz文件至index
git add .                                                   # 增加当前子目录下所有更改过的文件至index
git branch                                                  # 显示本地分支
git branch --contains 50089                                 # 显示包含提交50089的分支
git branch -a                                               # 显示所有分支（远端分支+本地分支）
git branch -r                                               # 显示所有远端分支
git branch --merged                                         # 显示所有已合并到当前分支的分支
git branch --no-merged                                      # 显示所有未合并到当前分支的分支
git branch -m master master_copy                            # 本地分支改名
git branch -dr [remote/branch]                              # 删除远程分支
git branch -d hotfixes/BJVEP933                             # 删除分支hotfixes/BJVEP933
git branch -D [branch-name]                                 # 强制删除分支branch-name
git branch --set-upstream dev origin/dev                    # 指定本地dev分支与远程origin/dev分支的链接
git branch [branch-name]                                     # 新建一个分支，但依然留在当前分支
git branch --track [branck-name] [remote-branch]             # 新建一个分支，与指定的远程分支建立追踪关系
git checkout [file]                                         # 恢复暂存区的指定文件到工作区
git checkout [commit] [file]                                 # 恢复某个commit的指定文件到暂存区和工作区
git checkout .                                                 # 恢复暂存区的所有文件到工作区
git checkout -b master_copy                                 # 新建一个分支，并切换到该分支
git checkout -b master master_copy                          # 上面的完整版
git checkout features/performance                           # 检出已存在的features/performance分支
git checkout --track [branch-name]                             # 新建一个与远程分支同名的分支，并切换到该分支
git checkout v2.0                             # 检出版本v2.0
git checkout -b devel origin/develop          # 从远程分支develop创建新本地分支devel并检出
git checkout -- README                     # 检出head版本的README文件（可用于修改错误回退）
git config user.name                                        # 查看config中配置的 user.name
git config --list                                            # 列出Git可以在该处找到的所有的设置 (或者 git config -l)
git config --global user.name &quot;xxx&quot;                         # 配置用户名
git config --global user.email &quot;xxx@xxx.com&quot;                # 配置邮件
git config --system --list                                  # 查看系统config
git config --global  --list                                 # 查看当前用户（global）配置    
git config --local  --list                                     # 查看当前仓库配置信息
git config --global color.ui true                           # git status等命令自动着色
git config --global color.status auto
git config --global color.diff auto
git config --global color.branch auto
git config --global color.interactive auto
git config --global --unset http.proxy   # remove  proxy configuration on git
git clone git+ssh://git@192.168.53.168/VT.git                 # clone远程仓库
git cherry-pick ff44785404a8e                                 # 合并提交ff44785404a8e的修改 (选择一个commit，合并进当前分支)
git commit -m &#x27;xxx&#x27;                                          # 提交
git commit --amend -m &#x27;xxx&#x27;                              # 合并上一次提交（用于反复修改）
git commit -am &#x27;xxx&#x27;                                     # 将add和commit合为一步
git diff                                                  # 显示所有未添加至暂存区的变更
git diff --cached                                 # 显示所有已添加index但还未commit的变更
git diff HEAD^                                          # 比较与上一个版本的差异
git diff HEAD -- ./lib                                 # 比较与HEAD版本lib目录的差异
git diff origin/master..master             # 比较远程分支master上有本地分支master上没有的
git diff origin/master..master --stat         # 只显示差异的文件，不显示具体内容
git fetch                                   # 获取所有远程分支（不更新本地分支，另需merge）
git fetch --prune                           # 获取所有原创分支并清除服务器上已删掉的分支
git fsck
git gc
git grep &quot;delete from&quot;                        # 文件中搜索文本“delete from”
git grep -e &#x27;#define&#x27; --and -e SORT_DIRENT
git init                                         # 初始化本地git仓库（创建新仓库）
git log                                                       # 显示提交日志
git log -1                                                    # 显示1行日志 -n为n行
git log -5
git log --stat                                                # 显示提交日志及相关变动文件
git log -p -m
git log v2.0                                                  # 显示v2.0的日志
git log --pretty=oneline                                     # 只显示版本号和说明
git log --pretty=format:&#x27;%h %s&#x27; --graph                       # 图示提交日志
git ls-files                                                  # 列出git index包含的文件
git ls-tree HEAD                                              # 内部命令：显示某个git对象
git merge origin/master                   # 合并远程master分支至当前分支
git merge [branch-name]                                     # 合并指定分支到当前分支
git mv README README2                                     # 重命名文件README为README2

git pull origin master                  # 获取远程分支master并merge到当前分支
git push origin HEAD                                        # 将当前分支push到远程dev分支
# git push &lt;远程主机名&gt; &lt;本地分支名&gt;:&lt;远程分支名&gt;             前面的是本地分支名，后面的是远程分支名，
git push origin dev                        # 将当前分支push到远程dev分支 （同名可以省略冒号部分）
git push origin master           # 将当前分支push到远程master分支
git push origin :hotfixes/BJVEP933    
# 删除远程仓库的hotfixes/BJVEP933分支 效果等同于（git push origin --delete hotfixes/BJVEP933    ）
git push origin --delete hotfixes/BJVEP933     # 删除远程仓库的hotfixes/BJVEP933分支
git push origin :refs/tags/v0.9                                  # 删除远程远程仓库的refs/tags/v0.9分支
git push origin --tags                          # 把所有tag推送到远程仓库 （git push 的时候不会推送tag，可以使用这个命令推送tag）
git push --force origin                                          
# git push的时候需要本地先git pull更新到跟服务器版本一致，如果本地版本库比远程服务器上的低，那么一般会提示你git pull更新，如果一定要提交，那么可以使用这个命令
git push --all origin                                           # 当遇到这种情况就是不管是否存在对应的远程分支，将本地的所有分支都推送到远程主机，这时需要 -all 选项
git revert dfb02e6e4f2f7b573337763e5c0013802e392818           # 撤销提交dfb02e6e4f2f7b573337763e5c0013802e392818
git rev-parse v2.0                                            # 内部命令：显示某个ref对应的SHA1 HASH
git reflog                                                  # 显示所有提交，包括孤立节点
git rm xxx                                                  # 删除index中的文件
git rm -r *                                                 # 递归删除
git rm --cached [file]                                        # 停止追踪指定文件，但该文件会保留在工作区
git remote add origin git+ssh://git@192.168.53.168/VT.git   
# 增加远程定义（用于push/pull/fetch）(本地git仓库关联GitHub仓库)
git reset --hard HEAD                     # 将当前版本重置为HEAD（通常用于merge失败回退）
git reset --hard 6fcfc89                                    # 版本回退到6fcfc89版本
git reset [file]                                 # 重置暂存区的指定文件，与上一次commit保持一致，但工作区不变
git reset [commit]                             # 重置当前分支的指针为指定commit，同时重置暂存区，但工作区不变
git reset --hard [commit]                                     
# 重置当前分支的HEAD为指定commit，同时重置暂存区和工作区，与指定commit一致
git reset --keep [commit]                                     
# 重置当前HAED为指定commit，但保持暂存区和工作区不变

git rebase
git show HEAD@{5}
git show master@{yesterday}                                   # 显示master分支昨天的状态
git show v2.0                                                 # 显示v2.0的日志及详细内容
git show dfb02e6e4f2f7b573337763e5c0013802e392818            # 显示某个提交的详细内容
git show dfb02                                                # 可只用commitid的前几位
git show HEAD                                                # 显示HEAD提交日志
git show HEAD^           # 显示HEAD的父（上一个版本）的提交日志 ^^为上两个版本 ^5为上5个版本
git status               # 查看当前版本状态（是否修改）
git show HEAD~3
git show -s --pretty=raw 2be7fcb476
git stash                                             # 暂存当前修改，将所有至为HEAD状态
git stash list                                                # 查看所有暂存
git stash show -p stash@{0}                                   # 查看第一次暂存
git stash apply stash@{0}                                     # 应用第一次暂存
git stash drop
git stash pop
git show-branch                                               # 图示当前分支历史
git show-branch --all                                          # 图示所有分支历史
git tag                                                       # 显示已存在的tag
git tag -a v2.0 -m &#x27;xxx&#x27;                                       # 增加v2.0的tag
git tag v1.0                                                   # 新建标签v1.0
git tag v1.0  25656e2                                           # 给commit id 为25656e2的历史版本打标签        
git tag -d v0.9                                                  # 删除本地                
git whatchanged                                     # 显示提交历史对应的文件修改
</code></pre></div><p style="text-align:right"><a href="https://waitforu.tech/posts/develope/git-workflow#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://waitforu.tech/posts/develope/git-workflow</link><guid isPermaLink="true">https://waitforu.tech/posts/develope/git-workflow</guid><dc:creator><![CDATA[Cody]]></dc:creator><pubDate>Fri, 25 Jul 2025 04:20:34 GMT</pubDate></item></channel></rss>