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:
@@ -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
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -417,8 +417,7 @@ int receiver_process_pending(Config* config, int file_descriptor, const Receiver
|
||||
--max-delete-capped commit still succeeds and the transfer proceeds;
|
||||
the terminal success frame reports the cap. */
|
||||
size_t deleted = 0;
|
||||
DeletePathObserver observer =
|
||||
sink->deleted_paths ? receiver_record_deleted_path : NULL;
|
||||
DeletePathObserver observer = sink->deleted_paths ? receiver_record_deleted_path : NULL;
|
||||
DeleteCommitResult deletion =
|
||||
(config->use_delete || config->delete_missing_args)
|
||||
? manifest_delete_all_observed(config, manifest, &deleted, observer,
|
||||
@@ -506,8 +505,7 @@ int receiver_process_pending(Config* config, int file_descriptor, const Receiver
|
||||
deferred_manifest = NULL;
|
||||
} else {
|
||||
size_t deleted = 0;
|
||||
DeletePathObserver observer =
|
||||
sink->deleted_paths ? receiver_record_deleted_path : NULL;
|
||||
DeletePathObserver observer = sink->deleted_paths ? receiver_record_deleted_path : NULL;
|
||||
DeleteCommitResult deletion = manifest_delete_all_observed(
|
||||
config, deferred_manifest, &deleted, observer, (void*)sink->deleted_paths);
|
||||
receiver_tally_deleted(sink, deleted);
|
||||
|
||||
+5
-8
@@ -956,11 +956,9 @@ void handler(int file_descriptor) {
|
||||
server-contacting --dry-run deletes nothing (no manifest is sent). */
|
||||
if (context->deferred_manifest) {
|
||||
size_t deleted = 0;
|
||||
DeletePathObserver observer =
|
||||
config->report_deletes ? receiver_record_deleted_path : NULL;
|
||||
DeleteCommitResult deletion =
|
||||
manifest_delete_all_observed(config, context->deferred_manifest, &deleted, observer,
|
||||
(void*)context->deleted_paths);
|
||||
DeletePathObserver observer = config->report_deletes ? receiver_record_deleted_path : NULL;
|
||||
DeleteCommitResult deletion = manifest_delete_all_observed(
|
||||
config, context->deferred_manifest, &deleted, observer, (void*)context->deleted_paths);
|
||||
context->stats.deleted_files += deleted;
|
||||
if (deletion == DELETE_COMMIT_ERROR) {
|
||||
transfer_ok = false;
|
||||
@@ -979,9 +977,8 @@ void handler(int file_descriptor) {
|
||||
/* Defence in depth (the enclosing block already excludes dry-run): a
|
||||
-n run never commits a deletion. */
|
||||
if (config->report_deletes)
|
||||
delete_plan_session_set_delete_observer(context->deferred_plans,
|
||||
receiver_record_deleted_path,
|
||||
(void*)context->deleted_paths);
|
||||
delete_plan_session_set_delete_observer(
|
||||
context->deferred_plans, receiver_record_deleted_path, (void*)context->deleted_paths);
|
||||
DeleteCommitResult deletion =
|
||||
config->dry_run ? DELETE_COMMIT_OK
|
||||
: delete_plan_session_commit(context->deferred_plans, config);
|
||||
|
||||
+2
-2
@@ -268,8 +268,8 @@ typedef enum SuperMode { SUPER_MODE_AUTO = 0, SUPER_MODE_ON = 1, SUPER_MODE_OFF
|
||||
* real (non-dry-run) deletion. It is set only when --info=del is requested with
|
||||
* --delete; the transfer decision itself is unchanged. */
|
||||
#define CONFIG_WIRE_OUTPUT_FIELDS(X) \
|
||||
X(report_dest_info, bool, false, BOOL) X(report_stats, bool, false, BOOL) \
|
||||
X(report_deletes, bool, false, BOOL)
|
||||
X(report_dest_info, bool, false, BOOL) \
|
||||
X(report_stats, bool, false, BOOL) X(report_deletes, bool, false, BOOL)
|
||||
|
||||
/* Codec-negotiation wave (protocol 2.26.0). compression_algo is the concrete
|
||||
* codec the client selected for this transfer (a CompressionAlgo id) and is the
|
||||
|
||||
@@ -791,9 +791,9 @@ static bool apply_missing(DeletePlanSession* session, const Config* config) {
|
||||
size_t deleted = 0;
|
||||
size_t skipped = 0;
|
||||
bool limit = false;
|
||||
bool ok = manifest_delete_missing_args_limited_observed(
|
||||
config, &manifest, remaining, &deleted, &skipped, &limit, session->observer,
|
||||
session->observer_context);
|
||||
bool ok = manifest_delete_missing_args_limited_observed(config, &manifest, remaining, &deleted,
|
||||
&skipped, &limit, session->observer,
|
||||
session->observer_context);
|
||||
session->deleted += deleted;
|
||||
session->planned += deleted;
|
||||
session->skipped += skipped;
|
||||
@@ -893,8 +893,8 @@ static bool apply_deferred_path(DeletePlanSession* session, const Config* config
|
||||
return ok;
|
||||
}
|
||||
|
||||
void delete_plan_session_set_delete_observer(DeletePlanSession* session, DeletePathObserver observer,
|
||||
void* context) {
|
||||
void delete_plan_session_set_delete_observer(DeletePlanSession* session,
|
||||
DeletePathObserver observer, void* context) {
|
||||
if (!session)
|
||||
return;
|
||||
session->observer = observer;
|
||||
|
||||
@@ -84,7 +84,7 @@ size_t delete_plan_session_deleted(const DeletePlanSession* session);
|
||||
truly removes (including the deferred --delete-delay commit), so the receiver
|
||||
can report rsync's `deleting PATH` lines through the terminal STATUS_STATS
|
||||
record. Pass NULL/0 to clear. */
|
||||
void delete_plan_session_set_delete_observer(DeletePlanSession* session, DeletePathObserver observer,
|
||||
void* context);
|
||||
void delete_plan_session_set_delete_observer(DeletePlanSession* session,
|
||||
DeletePathObserver observer, void* context);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3261,10 +3261,9 @@ static bool delete_extras_budgeted_observed(const Config* config, DeleteManifest
|
||||
remaining = budget->max_delete - budget->deleted;
|
||||
size_t deleted = 0;
|
||||
size_t skipped = 0;
|
||||
DeleteWalkResult result =
|
||||
delete_extras_limited_observed(config->receive_root_directory, manifest->keeps, manifest->dirs,
|
||||
remaining, skips, used, &deleted, &skipped, observer,
|
||||
observer_context);
|
||||
DeleteWalkResult result = delete_extras_limited_observed(
|
||||
config->receive_root_directory, manifest->keeps, manifest->dirs, remaining, skips, used,
|
||||
&deleted, &skipped, observer, observer_context);
|
||||
if (owned_prefixes) {
|
||||
for (int i = 0; i < config->basis_count; i++)
|
||||
free(owned_prefixes[i]);
|
||||
@@ -3592,8 +3591,8 @@ bool manifest_delete_missing_args_limited_observed(const Config* config, DeleteM
|
||||
void* observer_context) {
|
||||
DeleteBudgetState budget = {
|
||||
.max_delete = max_delete, .deleted = 0, .skipped = 0, .limit_hit = false};
|
||||
bool ok = delete_missing_args_budgeted_observed(config, manifest, &budget, observer,
|
||||
observer_context);
|
||||
bool ok =
|
||||
delete_missing_args_budgeted_observed(config, manifest, &budget, observer, observer_context);
|
||||
if (deleted)
|
||||
*deleted = budget.deleted;
|
||||
if (skipped)
|
||||
|
||||
@@ -132,7 +132,8 @@ bool manifest_delete_missing_args_limited(const Config* config, DeleteManifest*
|
||||
bool manifest_delete_missing_args_limited_observed(const Config* config, DeleteManifest* manifest,
|
||||
size_t max_delete, size_t* deleted,
|
||||
size_t* skipped, bool* limit_hit,
|
||||
DeletePathObserver observer, void* observer_context);
|
||||
DeletePathObserver observer,
|
||||
void* observer_context);
|
||||
/* Outcome of committing a delete manifest. LIMIT_REACHED reports rsync's
|
||||
partial --max-delete result: the budget allowed some deletions and the rest
|
||||
were skipped (the run still stores all file data but the client exits 25). */
|
||||
|
||||
+2
-1
@@ -876,7 +876,8 @@ DeleteWalkResult delete_extras_limited_observed(const char* dest_root, const Arr
|
||||
const ArrayList* synced_dirs, size_t max_delete,
|
||||
const DeleteSkipEntry* skips, int skip_count,
|
||||
size_t* deleted_out, size_t* skipped_out,
|
||||
DeletePathObserver observer, void* observer_context) {
|
||||
DeletePathObserver observer,
|
||||
void* observer_context) {
|
||||
if (deleted_out)
|
||||
*deleted_out = 0;
|
||||
if (skipped_out)
|
||||
|
||||
+2
-1
@@ -145,7 +145,8 @@ DeleteWalkResult delete_extras_limited_observed(const char* dest_root, const Arr
|
||||
const ArrayList* synced_dirs, size_t max_delete,
|
||||
const DeleteSkipEntry* skips, int skip_count,
|
||||
size_t* deleted_out, size_t* skipped_out,
|
||||
DeletePathObserver observer, void* observer_context);
|
||||
DeletePathObserver observer,
|
||||
void* observer_context);
|
||||
|
||||
DeleteWalkResult delete_extras_limited(const char* dest_root, const ArrayList* manifest,
|
||||
const ArrayList* synced_dirs, size_t max_delete,
|
||||
|
||||
Reference in New Issue
Block a user