57 lines
1.7 KiB
JavaScript
57 lines
1.7 KiB
JavaScript
/**
|
|
* Parse rsync --itemize-changes output into a structured change list.
|
|
*
|
|
* rsync itemize format: 11-character code + space + path
|
|
*
|
|
* Code structure: YXcstpoguax
|
|
* Y = update type: > (transfer), * (message/delete), c (local change), . (no update), h (hard link)
|
|
* X = file type: f (file), d (dir), L (symlink), D (device), S (special)
|
|
* remaining chars = what changed (size, time, perms, etc.) or '+++++++++' for new
|
|
*
|
|
* We care about:
|
|
* >f... = file transferred (new or modified)
|
|
* *deleting = file deleted
|
|
* cd... = directory (ignored for delta purposes)
|
|
*/
|
|
|
|
/**
|
|
* @typedef {{ status: 'added'|'modified'|'deleted', path: string }} Change
|
|
*/
|
|
|
|
/**
|
|
* Parse rsync --itemize-changes stdout into a list of file changes.
|
|
* @param {string} output
|
|
* @returns {Change[]}
|
|
*/
|
|
export function parseItemize(output) {
|
|
const changes = [];
|
|
|
|
for (const raw of output.split('\n')) {
|
|
const line = raw.trimEnd();
|
|
if (!line) continue;
|
|
|
|
// Deleted files: "*deleting path/to/file"
|
|
if (line.startsWith('*deleting ')) {
|
|
const path = line.slice('*deleting '.length).trimStart();
|
|
// Skip directory deletions (trailing slash)
|
|
if (!path.endsWith('/')) {
|
|
changes.push({ status: 'deleted', path });
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// File transfers: ">f......... path" (new or modified)
|
|
if (line.length > 12 && line[0] === '>' && line[1] === 'f') {
|
|
const code = line.slice(0, 11);
|
|
const path = line.slice(12);
|
|
const isNew = code.slice(2) === '+++++++++';
|
|
changes.push({ status: isNew ? 'added' : 'modified', path });
|
|
continue;
|
|
}
|
|
|
|
// Everything else (dirs, symlinks, attribute-only changes) — ignore
|
|
}
|
|
|
|
return changes;
|
|
}
|