diff --git a/extend/base/admin/service/AdminUpdateServiceBase.php b/extend/base/admin/service/AdminUpdateServiceBase.php index c1f8fd1..b32c62a 100644 --- a/extend/base/admin/service/AdminUpdateServiceBase.php +++ b/extend/base/admin/service/AdminUpdateServiceBase.php @@ -31,6 +31,7 @@ class AdminUpdateServiceBase public $forceConflict = null; // null=未指定(走交互), overwrite|skip|ask public $showScope = null; // null=未指定(默认all), all|conflict public $keepRepo = false; // dry-run模式下保留上游克隆目录 + public $fetchOnly = false; // 仅拉取上游代码,不执行对比和更新 public $skippedConflictFiles = []; // [file_path => ['type' => 'add|delete|update', 'category' => 'optional|force']] /** @@ -112,8 +113,10 @@ class AdminUpdateServiceBase if ($last_version == $current_version) { $output->writeln('当前版本为最新版本'); - if ((bool)$input->getOption('reinstall')) { - $output->writeln('重装代码'); + if ((bool)$input->getOption('reinstall') || $this->fetchOnly) { + if ((bool)$input->getOption('reinstall')) { + $output->writeln('重装代码'); + } } else { $this->cleanWorkpaceDir(); @@ -187,6 +190,73 @@ class AdminUpdateServiceBase $changed_files[$file_path] = 'update'; } + // 纯拉取模式:过滤实际变更、输出目录路径和变更清单,不执行任何更新 + if ($this->fetchOnly) { + // 关键:$changed_files['update'] 来自 array_intersect(第184行),包含两个版本共有的所有文件 + // 需进一步用 PathTools::compareFiles 做内容比较,过滤掉内容实际未变化的文件 + $actual_changes = []; + foreach ($changed_files as $fp => $type) { + if ($type === 'update') { + $now_fp = $now_dir . '/' . $fp; + $last_fp = $last_version_dir . '/' . $fp; + if (file_exists($now_fp) && file_exists($last_fp) + && !PathTools::compareFiles($now_fp, $last_fp)) { + $actual_changes[$fp] = $type; + } + } else { + // add/delete 始终是实际差异(基于 array_diff,不存在内容比较问题) + $actual_changes[$fp] = $type; + } + } + $changed_files = $actual_changes; + + $output->writeln(''); + $output->writeln('[纯拉取模式] 上游代码已就绪,未执行任何更新。'); + $output->writeln(' 当前版本: ' . $current_version); + $output->writeln(' 最新版本: ' . $last_version); + $output->writeln(''); + $output->writeln('代码目录:'); + $output->writeln(' 当前版本原始代码: ' . $current_version_dir); + $output->writeln(' 上游最新代码: ' . $last_version_dir); + $output->writeln(' 开发者代码: ' . $now_dir); + $output->writeln(''); + $add_count = count(array_filter($changed_files, fn($t) => $t === 'add')); + $delete_count = count(array_filter($changed_files, fn($t) => $t === 'delete')); + $update_count = count(array_filter($changed_files, fn($t) => $t === 'update')); + $output->writeln("变更清单: 新增 {$add_count}, 删除 {$delete_count}, 更新 {$update_count}"); + if (count($changed_files) > 5) { + $groups = []; + $dir_order = ['extend/base/', 'extend/think/', 'app/', 'config/', 'route/', 'public/', 'database/', 'composer']; + foreach ($changed_files as $fp => $type) { + $group_name = '其他'; + foreach ($dir_order as $prefix) { + if (str_starts_with($fp, $prefix)) { + $group_name = $prefix; + break; + } + } + if (strpos($fp, '/') === false) { + $group_name = '根目录'; + } + $groups[$group_name][$fp] = $type; + } + foreach ($groups as $group_name => $files) { + $output->writeln(" [{$group_name}]"); + foreach ($files as $fp => $type) { + $output->writeln(" [{$type}] " . $fp); + } + } + } else { + foreach ($changed_files as $fp => $type) { + $output->writeln(" [{$type}] " . $fp); + } + } + $output->writeln(''); + $output->writeln('请自行对比三份代码,手动合并所需的变更。'); + $output->writeln('完成后删除 runtime/update/ 目录,或下次运行 admin:update 时自动清理。'); + return; + } + // 提示用户有一些完全跟踪的文件被修改了 $optional_update_waring_files = []; diff --git a/extend/base/common/command/admin/UpdateBase.php b/extend/base/common/command/admin/UpdateBase.php index 27d411b..da5598c 100644 --- a/extend/base/common/command/admin/UpdateBase.php +++ b/extend/base/common/command/admin/UpdateBase.php @@ -29,6 +29,7 @@ class UpdateBase extends Command ->addOption('force-conflict', null, Option::VALUE_OPTIONAL, '强制文件冲突处理策略: overwrite|skip|ask') ->addOption('show', null, Option::VALUE_OPTIONAL, '变更输出范围: all|conflict') ->addOption('keep-repo', null, Option::VALUE_NONE, '预览模式下保留上游克隆目录,便于手动对比跳过的文件') + ->addOption('fetch-only', null, Option::VALUE_NONE, '仅拉取上游代码到本地,不执行对比和更新') ->setDescription('更新系统代码'); } @@ -51,6 +52,7 @@ class UpdateBase extends Command $update_service->forceConflict = $input->getOption('force-conflict') ?: null; $update_service->showScope = $input->getOption('show') ?: null; $update_service->keepRepo = (bool)$input->getOption('keep-repo'); + $update_service->fetchOnly = (bool)$input->getOption('fetch-only'); $update_service->update(); }