test(parity): option-wave rsync differentials + docs (109/21/26)

- tests/integration/test_option_parity.py: bwlimit parse matrix + throttle
  rate, --info flist/name/nonreg/del(dry+real+itemize)/remove, real-setpriv
  --ignore-errors, rsync-daemon -M forwarding evidence, filter protect/risk
  destination-only divergence pin.
- RSYNC_COMPAT.md: tally 109/21/26; --bwlimit and --ignore-errors -> Parity,
  --filter and -M -> Divergent with differential rationale, --info residual
  narrowed to the categories with no client-observable event.
- HANDOFF/README: protocol 2.27.0, option-wave summary.
This commit is contained in:
2026-09-18 21:16:12 +02:00
parent 6a129b54d4
commit eb7e3fd2e0
19 changed files with 546 additions and 62 deletions
+3 -4
View File
@@ -70,10 +70,9 @@ static bool strbuf_append(StrBuf* buf, const char* text) {
}
bool change_list_enabled(const Config* config) {
return config != NULL &&
(config->itemize_changes || config->out_format != NULL ||
(config->log_file != NULL && config->log_file_format != NULL) ||
(config->info_level & LOG_INFO_NAME) != 0);
return config != NULL && (config->itemize_changes || config->out_format != NULL ||
(config->log_file != NULL && config->log_file_format != NULL) ||
(config->info_level & LOG_INFO_NAME) != 0);
}
/* ---- Itemize code ---- */
+10 -10
View File
@@ -501,7 +501,9 @@ static bool is_accepted_debug_category(const char* name) {
static bool is_accepted_info_category(const char* name) {
static const char* const categories[] = {
"mount", "syms", "symsafe",
"mount",
"syms",
"symsafe",
};
for (size_t i = 0; i < sizeof(categories) / sizeof(categories[0]); i++) {
if (strcmp(name, categories[i]) == 0)
@@ -1846,9 +1848,9 @@ static int parse_bwlimit_value(const char* value, unsigned long long* bytes_per_
long long mult;
while (*arg >= '0' && *arg <= '9')
arg++;
if (*arg != '\0' &&
(*arg == '.' || *arg == localeconv()->decimal_point[0]))
for (arg++; *arg >= '0' && *arg <= '9'; arg++) {}
if (*arg != '\0' && (*arg == '.' || *arg == localeconv()->decimal_point[0]))
for (arg++; *arg >= '0' && *arg <= '9'; arg++) {
}
char suffix = *arg && *arg != '+' && *arg != '-' ? *arg++ : 'K';
switch (suffix) {
@@ -1932,8 +1934,7 @@ static int set_bwlimit_option(const char* value) {
if (parse_bwlimit_value(value, &bytes_per_sec) != 0)
return -1;
io_set_bwlimit(bytes_per_sec);
log_info_message(LOG_INFO_MISC, "Set bandwidth limit to %llu KB/s",
bytes_per_sec / 1024);
log_info_message(LOG_INFO_MISC, "Set bandwidth limit to %llu KB/s", bytes_per_sec / 1024);
return 0;
}
@@ -2651,10 +2652,9 @@ static int cli_finalize_config(Config* config, bool verbose, bool no_delta, bool
/* --info=del on a real --delete run asks the receiver to report the paths it
actually removed; the report rides the STATUS_STATS path list, so the wire
stats frame must be negotiated too. */
config->report_deletes =
config->use_delete && !config->dry_run &&
((config->info_level & LOG_INFO_DEL) != 0 || config->itemize_changes ||
config->out_format != NULL);
config->report_deletes = config->use_delete && !config->dry_run &&
((config->info_level & LOG_INFO_DEL) != 0 || config->itemize_changes ||
config->out_format != NULL);
config->report_stats = config->stats || config->show_progress ||
(config->info_level & LOG_INFO_PROGRESS) || format_needs_wire ||
config->report_deletes || (config->dry_run && config->use_delete);
+4 -3
View File
@@ -347,8 +347,8 @@ static void print_delete_reports(const Config* config, const ArrayList* paths) {
}
static void client_progress_begin(const Config* config) {
g_progress_active = (config->show_progress || info_flag_enabled(config, LOG_INFO_PROGRESS)) &&
!config->quiet;
g_progress_active =
(config->show_progress || info_flag_enabled(config, LOG_INFO_PROGRESS)) && !config->quiet;
g_progress_xferred = 0;
g_progress_seen = 0;
if (!g_progress_active) {
@@ -3307,7 +3307,8 @@ int send_files(Config* config) {
/* rsync default: a scan I/O error suppresses deletion unless
--ignore-errors, even in the late (commit) modes. Drop the keep-set so
the receiver removes nothing; the readable tree still transferred. */
bool late_delete = (manifest || config->delete_missing_args) && !delete_early && !delete_per_dir;
bool late_delete =
(manifest || config->delete_missing_args) && !delete_early && !delete_per_dir;
if (late_delete && !ignore_errors_allows_delete(config, had_scan_io)) {
log_message(LOG_LEVEL_WARNING, "IO error encountered -- skipping file deletion");
if (manifest) {
+1 -1
View File
@@ -218,7 +218,7 @@ typedef struct {
int completed;
Chunk* initial_chunk;
ProtocolSession* allocation_session;
FilterNode* root_filter_node; /* root .rsync-filter context (owned by ps) */
FilterNode* root_filter_node; /* root .rsync-filter context (owned by ps) */
const ScannerOptions* options; /* borrowed scan options (--info=nonreg output) */
} ParallelScanner;