
授权与合规声明本文全部操作对象均为自建隔离靶场本机容器或隔离虚拟机涉及安全测试的环节必须以取得合法授权为前提。未经授权的渗透测试违反《中华人民共和国网络安全法》与《刑法》相关条款须承担相应法律责任。本文只讲环境配置、版本对照与靶场隔离不含任何攻击步骤、利用载荷与绕过手法请勿将文中环境指向任何非自有系统。一、现象clone 成功了进去却发现文件不对劲1.1 三种很常见的画面跟着教程学靶场第一步几乎都是把仓库 clone 到本地。命令跑完没有报错看起来一切正常。可你cd进去之后画风就变了。常见的有三种。第一种某个子目录是空的。你记得教程里说这个目录里有东西打开一看里面空得干干净净连一个文件都没有。第二种文件在但内容不像真的。目录结构看着齐全可点开其中一个文件里面只有短短几行读起来像某种说明或占位和教程截图里那个完整的样子对不上。第三种重来一次又不一样。换个位置重新 clone或者删掉重来一遍同样的仓库进去看到的又和上次不太一样。先说清一条纪律本文不会写出任何具体的报错文本或日志原文。官方文档只说了clone 完成之后需要初始化并克隆其内部的子模块这件事本身并没有描述任何失败表现所以任何会出现某种提示的说法都属于编造。本文只做现象类描述本文未实测这些现象在你机器上的具体表现请以你本机为准。1.2 先把一句话立住clone 成功不等于文件到位看到上面这些画面第一反应往往是是不是我网断了“是不是 Git 装坏了”“是不是命令敲错了”。于是重装 Git、换网络、换镜像、删掉重来一圈折腾下来问题还在原地。在动手之前有一句判断值得先立住clone 成功不等于文件到位。要真正用上这句话得先把缺文件拆成两种完全不同的东西一种是根本没取回来另一种是取回来了但它不是你以为的那种东西。第一种有可能真的是网络或操作出了岔子值得去查第二种则很可能是这个仓库设计如此——内容本来就被放在别的地方clone 只把指路牌取回来了。两种混在一起查时间就白白花在重装 Git 上了。画面容易以为的实际更可能是某个子目录是空的网络断了、clone 没成功内容放在另一个仓库里需要额外一步取回文件在但只有几行仓库被精简过、教程过时它是指针真正内容不在这个文件里重来一次又不一样仓库本身不稳定两次取回的东西不同与操作步骤有关1.3 本文解决什么明确不解决什么本文只解决一个问题文件为什么没到位。至于一个仓库的顶层有哪些目录、每个目录是干什么的那是另一篇文章的范围那篇讲拿到一个靶场仓库先看哪几个目录本文不复述目录清单只讲文件为什么不在。同样地包管理器那一层——版本范围怎么写、锁定文件怎么起作用——另有专篇本文讲的是仓库自身的内容分发层两者不是一回事不要互串。项目活跃度、开源许可证、提交时的凭据管理本文也都不涉及。顺着这个思路本文后面讲三种情况子模块第二章、Git LFS第三章、以及你以为它会自动转换但它不会第四章第五章给一张对照表第六章给一套确认顺序第七章收束。本章可以带走的一句文件没到位先别急着重装 Git先分清它是没取回来还是取回来了但不是你以为的那样。二、情况一子模块它是仓库里的仓库指针2.1 它不是文件是一条指针子模块这个概念名字起得很直白它是仓库里面又嵌着一个仓库。父仓库里那个位置本身并不存放子仓库的全部文件只记下这里应该挂上另一个仓库、挂在哪个位置。所以你 clone 父仓库时把这个位置取回来了但位置背后的内容并没有跟着一起回来——那个子目录看起来就是空的。这不是坏掉了。父仓库记录的是关系不是内容。理解这一点目录是空的这件事就不再是故障而是关系已经建立、内容还没落地的中间状态。2.2 官方文档把它单列成一组命令Git 官方文档给git submodule的一句话定位是「Inspects, updates and manages submodules.」检查、更新与管理子模块G01。官方愿意为它单开一个命令页说明子模块在 Git 里是一件需要专门处理的事不是 clone 顺手就替你办完的。这个命令不带参数时官方描述逐字是「With no arguments, shows the status of existing submodules.」G02——也就是说一个不带任何参数的git submodule作用就是把现有子模块的状态显示出来。这是最省事的一次自检先看看有没有子模块、它们现在是什么状态。另一个常被提到的子命令是add官方描述是「Add the given repository as a submoduleat the given path」G05——把给定的仓库作为子模块加在给定的路径上。这里有两个给定一个是仓库一个是路径两者都不是自动决定的。官方表述逐字说的是什么出处Inspects, updates and manages submodules.git submodule的整体定位G01With no arguments, shows the status of existing submodules.不带参数显示现有子模块状态G02Add the given repository as a submodule at the given pathadd是把仓库加在给定路径上G05After the clone is created, initialize and clone submodules within子模块的初始化与克隆在 clone 之后G032.3 关键子模块的取回是 clone 之后的另一件事这一节是本章的重点。git clone有一个参数叫--recurse-submodules官方对它的描述逐字是「After the clone is created, initialize and clone submodules within」——clone 完成之后在其内部初始化并克隆子模块G03。请注意这句话的语序。它没有说clone 的时候顺带把子模块也取了而是说clone 完成之后再去初始化并克隆里面的子模块。换句话说子模块的初始化与克隆本来就是 clone 之后的一个独立步骤--recurse-submodules做的只是让 clone 结束时顺手把这个独立步骤也跑一遍。你没加这个参数那一步自然就没有发生于是子目录是空的——这是设计使然不是你操作错了。如果你觉得每次都手动加参数麻烦官方文档还提到一个配置项clone.filterSubmodules可以把它设成默认开启G04一旦设好之后 clone 是否递归取子模块就有了一个默认值不必每条命令都手敲。⚠️ 代码待验证# 带递归参数的 clone官方描述为clone 完成之后在其内部 initialize and clone 子模块G03# ⚠️ 本文未在本机实测请以你本机实际情况为准gitclone --recurse-submodules仓库地址# 若已经 clone 过、想要补上子模块这一步# 不带参数的 submodule 用于显示现有子模块状态G02gitsubmodule⚠️ 代码待验证# 把一个给定仓库作为子模块加在给定路径上G05# ⚠️ 本文未在本机实测路径与用途请按你的实际工程填写gitsubmoduleadd子仓库地址目标路径# 把递归取子模块设为默认G04具体配置键名为 clone.filterSubmodules# ⚠️ 本文未在本机实测仅引官方文档说明该配置项存在2.4 关于版本一句话交代上面这些说法来自 Git 官方文档截至 2026-10-06git-submodule文档页标注的版本是2.54.0G06。Git 的版本会往前走文档也会跟着更新所以这一句2.54.0只是截至 2026-10-06的一个快照不是固定结论。你在自己的机器上核对时以你那一版的官方文档为准。本章可以带走的一句子模块记录的是关系不是内容它的初始化与克隆本来就是 clone 之后的独立一步——目录是空的多半是因为那一步还没走。三、情况二Git LFS大文件被换成了文本指针3.1 官方对它的定位大文件被替换为文本指针Git LFS 解决的是另一类问题仓库里有一些体积大的文件不适合直接塞进 Git 的常规存储。官方对它的定位逐字是Git LFS 「replaces large filessuch as audio samples, videos, datasets, and graphicswith text pointers inside Git, while storing the file contentson a remote serverlike GitHub.com or GitHub Enterprise.」L01把这句话拆开看它讲了两件事仓库里放的是 text pointers文本指针而真正的内容存在远端服务器上。所以你在本地看到的那个文件体积很小、内容也很短它确实在那儿但它只是指路牌——真正的大文件在远端需要工具按这根指针去取。这就是第一章第二种画面的来源文件在但你看到的不是内容而是指针。这不是文件损坏也不是被谁删了而是这个仓库有意把大文件放到了别处。3.2 启用一次是为用户账号做的光有这个设计还不够本地得有能读懂指针的工具。官方的做法是安装之后跑一次git lfs install并且特别说明这句话「You only need to run this once per user account.」你只需要为每个用户账号运行一次L02。这句话里两个词值得留意once一次和per user account按用户账号。意思是这个启用动作不是每进一个仓库都要做一遍而是针对你这个用户账号设一次就够了。所以如果你在一个新仓库里看到指针取不回来先别怀疑我是不是忘了在这个仓库里启用——按官方口径启用这件事本来就与单个仓库无关。3.3 由你指定哪些类型交给 LFS 管默认情况下Git LFS 并不知道哪些文件该走 LFS。要由你在每个要用 LFS 的仓库里指定。官方给出的做法是git lfs track *.psd也可以直接编辑.gitattributes并且可以随时继续追加文件扩展名。紧接着官方还有一句关键提醒要确保.gitattributes本身被跟踪git add .gitattributesL03。最后这半句很容易被忽略但它很重要决定哪些文件归 LFS 管的那份清单自己也必须进入版本控制。清单不被跟踪规则就只存在于你这一台机器上别人 clone 下来自然对不上。官方动作官方说明要点出处git lfs installYou only need to run this once per user account.L02git lfs track *.psd指定要托管的文件类型也可直接编辑.gitattributesL03git add .gitattributes要确保.gitattributes本身被跟踪L03git lfs migrate官方点名存在该命令用于把既有文件转换过来细节待验证L04⚠️ 代码待验证# 安装后为用户账号启用一次官方You only need to run this once per user account.L02# ⚠️ 本文未在本机实测请以你本机实际情况为准gitlfsinstall# 指定要交给 LFS 托管的文件类型例如 *.psdL03# 也可以直接编辑 .gitattributes效果是同一条规则# ⚠️ 本文未在本机实测gitlfs track*.psd# 关键一步让清单本身也被跟踪L03# ⚠️ 本文未在本机实测gitadd.gitattributes3.4 一条安全提示和当前下载版本官方站点上还挂着一条安全提示逐字是「Git LFS security update: All users should update to 3.7.1 or newer.」L06——注意这里用的是should官方给的是所有用户都应当升级到 3.7.1 或更新版本。这是一条建议升级的提示本文照原文引用不改它的情态。至于安装渠道官方站点提供的下载版本截至 2026-10-06是v3.8.0分别对应 Windows / Mac Intel / Mac Apple Silicon / Linux x86-64安装渠道里包含 Homebrewbrew install git-lfs、MacPortsport install git-lfs与 PackageCloudL05。和 2.54.0 一样v3.8.0 也是截至 2026-10-06的值会变。本章可以带走的一句Git LFS 把大文件换成放在 Git 里的文本指针、真正内容留在远端你看到文件很短是它的正常形态不是损坏。四、情况三你以为它会自动转换但它不会4.1 官方明确写下的一条边界前两章讲的都是东西放在别处得去取。这一章讲的是最容易误判的一类你以为它会顺手把老文件也转过来但它不会。官方对这件事讲得很清楚定义 Git LFS 要跟踪的文件类型其本身does not in itself不会把既有文件转换为 Git LFS——官方举的例子是其他分支上的文件、以及先前提交历史里的文件。要把这些既有文件转过来官方点名的是一条专门的命令git lfs migrateL04。也就是说用track指定了类型和老文件已经变成 LFS 托管是两件事。前者是给以后新增的文件定规则后者要动的是已经存在的内容。规则定好之后新文件按规则走而历史上已经存在的那批文件并不会因为你定了规则就自动追认过去。这里必须交代一条边界官方在这一页只点名git lfs migrate这个命令存在、并且用于把既有文件转换过来并未在本页给出它的具体行为与风险。因此本文只写到官方说明存在这个命令、用于把既有文件转换过来为止它的具体行为与风险本文不作描述属待验证BX05。4.2 设计如此和真出问题是两件事把这条边界讲透是为了立住一个判断习惯先确认是不是设计如此再去查环境。如果你在旧仓库里看到某些文件还是老样子、没有被 LFS 接管别急着怀疑工具坏了。对照官方这句话你面对的很可能是设计如此LFS 的规则只覆盖你定义它之后的行为历史上既有的那批文件本来就不在它的自动处理范围内。反过来判断真出问题也要有个准绳先把这个仓库的设计是什么样的弄清楚再用它去解释你看到的现象。拿一个设计如此的现象去查网络、查权限、查工具版本方向从一开始就错了。你做的事会转换既有文件吗官方依据定义 LFS 要跟踪的文件类型track或改.gitattributes不会does not in itselfL04想把其他分支、先前提交历史里的既有文件转过来需使用官方点名的git lfs migrate具体行为与风险待验证L04 / BX05本章可以带走的一句定规则管的是以后转旧文件是另一件事既有文件没被自动接管是设计如此不是故障。五、一张对照表三种情况怎么分把前三章收成一张表。判断时按现象 → 它是什么 → 官方依据 → 该怎么确认 → 属于哪一类一路往下看。现象它是什么官方依据该怎么确认设计如此还是真出问题某个子目录是空的父仓库只记了关系子仓库内容未取回G03clone 完成之后才 initialize and clone submodules within用不带参数的git submodule看现有子模块状态G02设计如此可加--recurse-submodules或设clone.filterSubmodules文件在但只有短短几行它是文本指针真正内容在远端L01replaces large files with text pointers inside Git看该仓库的 LFS 规则track/.gitattributesL03设计如此LFS 的正常形态旧文件没被 LFS 接管规则只覆盖以后不追认既有内容L04does not in itself 转换其他分支、先前提交历史里的文件确认这批文件是否在定义规则之前就已存在设计如此需git lfs migrate细节待验证这张表的用法很简单先找到你看到的现象在哪一行再看最后一列。如果落在设计如此那么正确的动作是按第四列去确认而不是去重装工具如果四列都对不上才轮到往环境、网络、权限这些方向去查。配套资料这张三种情况对照表连同子模块与 Git LFS 的官方表述逐字摘录、确认顺序清单一起收进资料包扫码即可获取本章可以带走的一句三种现象都先看设计如此这一列只有对不上才去查环境。六、一套可以照着做的确认顺序6.1 确认先做动手后做前三章的结论都是先确认。这一章把它整理成一套只读为主的顺序照着走一遍基本能把缺文件归到某一类里。第一步看结构。先弄清这个目录里应该有什么再看现在有什么。缺的是整块目录还是单个文件——这一步决定你往子模块方向看还是往 LFS 方向看。第二步看是不是指针形态。如果文件确实存在但内容很短、呈现为一种说明或占位的样子那它更可能是文本指针而不是真正的文件内容对应 L01。第三步看子模块状态。用不带参数的git submodule把现有子模块状态显示出来G02。有子模块、状态不对就往 clone 那一步找。第四步看 LFS 规则。确认这个仓库有没有定义 LFS 要跟踪的类型git lfs track或.gitattributesL03以及那份清单本身有没有被跟踪。规则只在本地、没进版本控制是常见的一处错位。第五步确认是不是既有内容。如果现象是旧文件没被接管对照 L04 判断这批文件是不是在规则定义之前就已存在——如果是那就是设计如此而不是故障。6.2 一段可以照抄的检查下面这段把上面五步里可执行的部分收在一起。所有命令本文都未在本机实测因此整段都带待验证标记。⚠️ 代码待验证# 第三步显示现有子模块状态G02# ⚠️ 本文未在本机实测输出形态请以你本机为准gitsubmodule# 第四步查看本仓库里 LFS 的相关规则规则文件是 .gitattributesL03# ⚠️ 本文未在本机实测cat.gitattributes# 第五步确认这份清单本身有没有被版本控制跟踪L03# ⚠️ 本文未在本机实测gitls-files .gitattributes⚠️ 代码待验证# 一条可选的完整路径在 clone 时就把子模块一并取回G03# 官方描述After the clone is created, initialize and clone submodules within# ⚠️ 本文未在本机实测请以你本机实际情况为准gitclone --recurse-submodules仓库地址# 若希望递归取子模块成为默认行为可用 clone.filterSubmodules 配置项G04# ⚠️ 本文未在本机实测仅引官方文档说明该配置项存在6.3 一条边界要交代清楚子模块那一层与 GitHub 平台的仓库页怎么显示有关例如页面上那个目录为什么被画成另一种样子、有没有直接的入口这些属于平台侧的操作本文不涉及也不做任何平台操作判断。本文只讲本地这一侧仓库 clone 下来之后内容为什么没到位。另外要再强调一次本文的纪律本文不写任何具体的报错或日志原文。官方文档在子模块这件事上只说了clone 完成之后要 initialize and clone submodules withinG03没有给任何失败表现描述因此本文不给任何会出现什么提示的说法这一类属待验证BX04。本章可以带走的一句五步顺序是看结构 → 看指针形态 → 看子模块状态 → 看 LFS 规则 → 看是不是既有内容全部以确认为主。七、给初学者的判断顺序以及收束7.1 三步先看结构再看大小最后看形态如果你只想记一套最省事的判断记这三步就够。第一步看结构。缺的是整块目录还是目录里某个位置该有东西却没有整块目录缺位先怀疑子模块这一层。第二步看文件大小与内容。文件在那儿、但明显不像一个真文件该有的分量内容也很短往指针形态这个方向想。第三步看是不是指针形态本身。如果是指针那么内容不在这个文件里就是它的正常表现接下来要确认的是指针背后的东西有没有条件取回来工具是否可用、规则是否到位。这三步之外还有一句更上位的话先确认是不是设计如此再去查环境。本文讲的三类情况全都落在设计如此这一侧。把它们当成故障去修方向从一开始就偏了。步骤看什么依据只读还是可写一缺的是整块目录还是单个位置子模块记录关系不记录内容只读二文件是否存在、内容是否明显偏短L01大文件被替换为文本指针只读三是否是指针形态、规则是否到位L03 / L04以只读为主改规则才涉及写入7.2 收束回到标题clone 下来却缺文件很多时候不是网络断了也不是你操作错了而是这个仓库的设计就是这样——内容被放在别的地方clone 只把指路牌取回来了。认清clone 成功 ≠ 文件到位你就不会把时间浪费在重装 Git 上再认清设计如此 ≠ 出错你就能在定规则只管以后、不追认既有内容这类现象面前稳住判断。真正需要去查环境的是那些四列都对不上的情形——那时候再动手方向才对。配套资料这套三步判断 五步确认的顺序连同本文引用的全部官方表述逐字摘录与出处对照一起收进资料包扫码即可获取本章可以带走的一句先看结构、再看大小、再看是不是指针形态先确认是不是设计如此再去查环境。附表 A本文引用事实与官方出处对照表#事实陈述逐字处已标引号一手出处来源名 URL核验日期本文位置1G01git submodule官方定位逐字「Inspects, updates and manages submodules.」Git 官方文档 git-scm.com/docs/git-submodule2026-10-06第二章2G02不带参数时逐字「With no arguments, shows the status of existing submodules.」同 G012026-10-06第二、六章3G03--recurse-submodules逐字「After the clone is created, initialize and clone submodules within」子模块初始化与克隆是 clone 之后的独立一步Git 官方文档 git-scm.com/docs/git-clone2026-10-06第二、五、六章4G04配置项clone.filterSubmodules可把--recurse-submodules设为默认开启同 G032026-10-06第二章5G05git submodule add逐字「Add the given repository as a submodule at the given path」同 G012026-10-06第二章6G06git-submodule文档页截至 2026-10-06标注 last updated in 2.54.0同 G012026-10-06第二章7L01Git LFS 官方定位逐字「replaces large files … with text pointers inside Git, while storing the file contents on a remote server」Git LFS 官方站点 git-lfs.com2026-10-06第三、五、七章8L02git lfs install逐字「You only need to run this once per user account.」同 L012026-10-06第三章9L03git lfs track *.psd或直接编辑.gitattributes并要确保.gitattributes本身被跟踪同 L012026-10-06第三、六章10L04定义 LFS 跟踪类型其本身不会转换既有文件其他分支、先前提交历史需用官方点名的git lfs migrate(1)同 L012026-10-06第四、五章11L05官方站点下载版本截至 2026-10-06为 v3.8.0渠道含 Homebrew / MacPorts / PackageCloud同 L012026-10-06第三章12L06安全提示逐字「Git LFS security update: All users should update to 3.7.1 or newer.」原文用 should同 L012026-10-06第三章13待验证BX04子模块在具体平台上的缺文件表现形态——官方只说clone 之后需 initialize and clone submodules within未给任何失败表现描述本文不写任何报错文本见表 B-补 BX042026-10-06第一、六章待验证14待验证BX05git lfs migrate的具体行为与风险——官方本页仅点名该命令存在、用于把既有文件转换过来未给行为细节见表 B-补 BX052026-10-06第四章待验证15本文未实测git clone --recurse-submodules/git submodule/git lfs install/git lfs track等命令本文均未在本机运行相关代码块一律保留代码待验证标记本文未实测2026-10-06第二、三、六、七章待验证附表 B术语速查表术语一句话解释clone把远端仓库取回本地取回成功不等于全部内容都到位子模块submodule仓库里嵌着的另一个仓库父仓库只记关系与位置不存它的内容git submodule官方定位为检查、更新与管理子模块不带参数时显示现有子模块状态--recurse-submodulesclone 的参数官方描述为 clone 完成之后在其内部初始化并克隆子模块clone.filterSubmodules配置项可把递归取子模块设为默认开启Git LFS把大文件替换为 Git 内的文本指针真正内容存在远端服务器文本指针text pointer本地看到的那个很短的文件它指向远端真正的内容git lfs install启用动作官方说明每个用户账号只需运行一次git lfs track指定哪些文件类型交给 LFS 托管也可直接编辑.gitattributes.gitattributes承载 LFS 规则的清单文件官方提醒它本身也要被跟踪git lfs migrate官方点名存在的命令用于把既有文件转换过来具体行为与风险待验证设计如此 / 真出问题本文的二分前者是仓库本来的工作方式后者才需要去查环境代码待验证 / 待验证前者指该命令未在本机实际运行后者指该项未实测或官方未给该层描述写在最后这篇用到的资料写这篇文章时我一直在想clone 明明成功了进去却空了一个目录这种别扭感——后来才想通那不是失败而是子模块和 Git LFS 这类机制本来就只把指路牌取回来顺手也整理了几份配套的东西靶场环境对照表DVWA、upload-labs 在 Windows / macOS / Linux 三平台的可行性与推荐路径Web 安全学习路线图从基础打牢到安全管理四个阶段各学什么常用靶场清单每个靶场练什么、适合哪个阶段资料是我自己整理的放在下面这个码上扫码即可获取添加时备注「靶场」优先通过。拿到之后建议先看环境对照表那一份再回头把本文第五章那张三种情况对照表过一遍下次再遇到clone 下来缺文件就知道先分清没取回来和取回来了但不是你以为的东西。