Summary
xsync re-uploads Office files (.docx, .xlsx, .pptx) on every run, even when the local source has not changed. The cause is server-side: SharePoint rewrites Office documents on upload to bind them to the library's content type, so the stored remote file never matches the local source byte-for-byte. xsync's change detection compares the remote against the local file, so it always concludes the file changed and re-uploads.
This is distinct from #3 (dropped fileSystemInfo mtime PATCH), but shares the same root assumption — that the remote reflects what was uploaded.
Environment
- xsync 1.5.1
- Target: SharePoint Online document library (Microsoft 365 / Graph)
- Observed while publishing built deliverables to a library via xsync.
What happens
After a clean upload, an immediate --dry-run wants to re-upload the same files:
$ xsync ./dist "https://tenant.sharepoint.com/Shared Documents/folder"
Done: 2 created, 2 copied, 0 deleted (0 up to date).
$ xsync --dry-run ./dist "https://tenant.sharepoint.com/Shared Documents/folder"
Dry run — no changes will be made:
upload Deliverable 1/Excelano Assessment.docx
upload Deliverable 1/Supporting/Inventory.xlsx
Would change: 0 created, 2 copied, 0 deleted (0 up to date).
Evidence: SharePoint mutates the file on upload
Downloading the just-uploaded .docx back and comparing it to the local source:
- Size changed on upload: 9769 bytes → 17216 bytes.
- The remote copy gained content-type binding parts that were never in the source:
customXml/item1.xml
customXml/item2.xml
customXml/item3.xml
customXml/itemProps1.xml
customXml/itemProps2.xml
customXml/itemProps3.xml
customXml/_rels/item1.xml.rels
customXml/_rels/item2.xml.rels
customXml/_rels/item3.xml.rels
docProps/custom.xml gained a ContentTypeId property:
<property fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}" pid="2" name="ContentTypeId">
<vt:lpwstr>0x010100ABAE2669E0DAFF4D82C93F91D713F107</vt:lpwstr>
</property>
- The actual document body (
word/document.xml) is byte-identical — only SharePoint's metadata was added. The deliverable content is intact; only the container bytes differ.
Because the size differs outright, the size+mtime fast path flags the file as changed before the v1.5.1 quickXorHash fallback ever applies — and even if sizes matched, the injected parts would change the hash. So the content-hash fallback cannot rescue this case.
Reproduction
- Build or take any local
.docx/.xlsx/.pptx.
xsync ./local "https://<tenant>.sharepoint.com/<library>/<folder>"
xsync --dry-run ./local "https://<tenant>.sharepoint.com/<library>/<folder>"
- Observe the files listed for re-upload.
- (Optional) Download the remote copy and
unzip -l it: the customXml/* parts and the ContentTypeId custom property are present and the byte size differs from the source.
Plain-text and other non-Office files are not rewritten and sync correctly.
Suggested direction
A stateless remote-vs-local comparison can't see through a server that mutates the file on ingest. The standard fix is a local last-synced-state cache (xsync already owns ~/.config/xsync/): record each uploaded path's local size/mtime/quickXorHash at upload time, and on subsequent runs skip when the local file is unchanged since then — independent of whatever SharePoint did to the remote. This is the rsync model.
A useful property: this would also subsume #3, since both issues stem from trusting the remote's mutable state rather than a record of what was sent.
Tradeoff to weigh: it makes xsync stateful (cache drift if the remote is edited out-of-band, or the cache is wiped). A conservative design keeps the existing stateless comparison authoritative and uses the cache only as a fast path to suppress redundant uploads when the local file is provably unchanged — never to skip a genuine change. On a cold cache it falls back to current behavior (one re-upload, then cached).
Summary
xsyncre-uploads Office files (.docx,.xlsx,.pptx) on every run, even when the local source has not changed. The cause is server-side: SharePoint rewrites Office documents on upload to bind them to the library's content type, so the stored remote file never matches the local source byte-for-byte. xsync's change detection compares the remote against the local file, so it always concludes the file changed and re-uploads.This is distinct from #3 (dropped
fileSystemInfomtime PATCH), but shares the same root assumption — that the remote reflects what was uploaded.Environment
What happens
After a clean upload, an immediate
--dry-runwants to re-upload the same files:Evidence: SharePoint mutates the file on upload
Downloading the just-uploaded
.docxback and comparing it to the local source:docProps/custom.xmlgained aContentTypeIdproperty:word/document.xml) is byte-identical — only SharePoint's metadata was added. The deliverable content is intact; only the container bytes differ.Because the size differs outright, the size+mtime fast path flags the file as changed before the v1.5.1 quickXorHash fallback ever applies — and even if sizes matched, the injected parts would change the hash. So the content-hash fallback cannot rescue this case.
Reproduction
.docx/.xlsx/.pptx.xsync ./local "https://<tenant>.sharepoint.com/<library>/<folder>"xsync --dry-run ./local "https://<tenant>.sharepoint.com/<library>/<folder>"unzip -lit: thecustomXml/*parts and theContentTypeIdcustom property are present and the byte size differs from the source.Plain-text and other non-Office files are not rewritten and sync correctly.
Suggested direction
A stateless remote-vs-local comparison can't see through a server that mutates the file on ingest. The standard fix is a local last-synced-state cache (xsync already owns
~/.config/xsync/): record each uploaded path's local size/mtime/quickXorHash at upload time, and on subsequent runs skip when the local file is unchanged since then — independent of whatever SharePoint did to the remote. This is the rsync model.A useful property: this would also subsume #3, since both issues stem from trusting the remote's mutable state rather than a record of what was sent.
Tradeoff to weigh: it makes xsync stateful (cache drift if the remote is edited out-of-band, or the cache is wiped). A conservative design keeps the existing stateless comparison authoritative and uses the cache only as a fast path to suppress redundant uploads when the local file is provably unchanged — never to skip a genuine change. On a cold cache it falls back to current behavior (one re-upload, then cached).