feat: add ll-cli analyze size subcommand#1721
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces the analyze size subcommand to the ll-cli utility, allowing users to view the disk space usage of installed modules (including exclusive, shared, logical, and actual sizes) and the repository's real size. The implementation adds CLI parsing, core size calculation logic, and both text and JSON output printers. The code review feedback highlights several important issues: performance bottlenecks caused by heap allocations during inode key string formatting, map overhead for non-hard-linked files, mathematical truncation errors during shared size division, lexicographical version sorting bugs, and column misalignment in the text printer when handling wide-character translations.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Add a new `analyze size` subcommand to ll-cli that reports on-disk usage
of installed layers, breaking each module's footprint into:
- exclusive: bytes unique to that module
- shared: bytes shared with at least one other module
- logical: exclusive + shared (apparent size to the module)
- actual: shared bytes divided across the sharing modules
Sizes are computed from st_blocks (real block usage, not file size),
and hard-linked inodes are deduplicated by (st_dev, st_ino) so they
are counted once per sharing module. A separate pass reports the
total real disk usage of the repository directory for cross-reference.
Options:
--sort {actual|logical|exclusive|shared|id} (default: actual)
--asc (default: descending)
Signed-off-by: reddevillg <reddevillg@gmail.com>
deepin pr auto review★ 总体评分:85分■ 【总体评价】
■ 【详细分析】
■ 【改进建议代码示例】 // 建议将重复的目录遍历逻辑提取为公共函数,放在匿名命名空间中
template<typename Func>
Result<void> traverseDirectory(const std::filesystem::path &dir, Func callback) noexcept
{
LINGLONG_TRACE("traverse directory");
std::error_code ec;
auto rootResult = callback(dir);
if (!rootResult) {
return LINGLONG_ERR(rootResult);
}
auto iterator = std::filesystem::recursive_directory_iterator{
dir,
std::filesystem::directory_options::skip_permission_denied,
ec
};
if (ec) {
return LINGLONG_ERR(fmt::format("failed to open directory {}", dir), ec);
}
const auto end = std::filesystem::recursive_directory_iterator{};
while (iterator != end) {
const auto &entry = *iterator;
auto result = callback(entry.path());
if (!result) {
return LINGLONG_ERR(result);
}
iterator.increment(ec);
if (ec) {
return LINGLONG_ERR(fmt::format("failed to iterate directory {}", dir), ec);
}
}
return LINGLONG_OK;
}
// 在 CLIPrinter::printModuleSizes 中修复对齐问题示例
void CLIPrinter::printModuleSizes(const std::vector<ModuleSizeInfo> &list,
std::uint64_t actualTotalSize,
std::uint64_t repoSize)
{
std::uint64_t totalExclusiveSize{ 0 };
std::uint64_t totalSharedSize{ 0 };
std::uint64_t totalLogicalSize{ 0 };
std::cout << "\033[38;5;214m" << std::left << adjustDisplayWidth(qUtf8Printable(_("ID")), 43)
<< adjustDisplayWidth(qUtf8Printable(_("Version")), 16)
<< adjustDisplayWidth(qUtf8Printable(_("Channel")), 16)
<< adjustDisplayWidth(qUtf8Printable(_("Module")), 14)
<< adjustDisplayWidth(qUtf8Printable(_("Exclusive")), 14)
<< adjustDisplayWidth(qUtf8Printable(_("Shared")), 14)
<< adjustDisplayWidth(qUtf8Printable(_("Logical")), 14) << qUtf8Printable(_("Actual"))
<< "\033[0m" << std::endl;
for (const auto &info : list) {
totalExclusiveSize += info.exclusiveSize;
totalSharedSize += info.sharedSize;
totalLogicalSize += info.logicalSize;
// 修复:使用 adjustDisplayWidth 代替 std::setw 以支持多字节中文字符对齐
std::cout << adjustDisplayWidth(qUtf8Printable(info.id + " "), 43)
<< adjustDisplayWidth(qUtf8Printable(info.version + " "), 16)
<< adjustDisplayWidth(qUtf8Printable(info.channel + " "), 16)
<< adjustDisplayWidth(qUtf8Printable(info.module + " "), 14)
<< adjustDisplayWidth(qUtf8Printable(formatSize(info.exclusiveSize) + " "), 14)
<< adjustDisplayWidth(qUtf8Printable(formatSize(info.sharedSize) + " "), 14)
<< adjustDisplayWidth(qUtf8Printable(formatSize(info.logicalSize) + " "), 14)
<< formatSize(info.actualSize) << std::endl;
}
std::cout << std::endl
<< _("Calculated logical total size: ") << formatSize(totalLogicalSize) << " ("
<< _("Exclusive: ") << formatSize(totalExclusiveSize) << ", " << _("Shared: ")
<< formatSize(totalSharedSize) << ")" << std::endl
<< _("Calculated actual total size: ") << formatSize(actualTotalSize) << std::endl
<< _("Repository real size: ") << formatSize(repoSize) << std::endl;
} |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: dengbo11, reddevillg The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
Add a new
analyze sizesubcommand to ll-cli that reports on-disk usage of installed layers, breaking each module's footprint into:Sizes are computed from st_blocks (real block usage, not file size), and hard-linked inodes are deduplicated by (st_dev, st_ino) so they are counted once per sharing module. A separate pass reports the total real disk usage of the repository directory for cross-reference.
Options:
--sort {actual|logical|exclusive|shared|id} (default: actual)
--asc (default: descending)