Skip to content

Tracked changes

A document edited with Word's "Track Changes" on carries its history: insertions in w:ins elements, deletions in w:del, and the same treatment for formatting changes, paragraph marks and table rows. This library reads that history and can accept or reject any part of it.

What Paragraph.text means here

Text in a document with tracked changes has two well-defined readings, and this library gives you both:

paragraph.text           # the document as it now reads
paragraph.original_text  # the document as it read before the changes

text includes insertions and excludes deletions — what you would get by accepting everything. original_text does the reverse. For a paragraph with no revisions the two are identical.

Neither is "the text with markup shown". Word displays deletions struck through alongside insertions, which is a rendering rather than a string.

Note

This changed in 2.0.0. The 0.9.x line dropped both insertions and deletions, so the result matched neither reading. Paragraph.runs likewise now includes runs inside a w:ins.

Reading the revisions

Document.revisions returns every revision in the body, in document order:

from docx import Document

document = Document("reviewed.docx")

for revision in document.revisions:
    print(revision.type, revision.author, revision.date, repr(revision.text))
WD_REVISION_TYPE.INSERTION Ada Lovelace 2026-03-14 09:12:00 'and therefore '
WD_REVISION_TYPE.DELETION Charles Babbage 2026-03-14 10:03:00 'possibly '

Each Revision carries:

Paragraph.revisions narrows this to one paragraph.

Who changed what

authors = {revision.author for revision in document.revisions}

Accepting and rejecting

Individually:

for revision in document.revisions:
    if revision.author == "Ada Lovelace":
        revision.accept()
    else:
        revision.reject()

Accepting an insertion keeps the text and drops the marker; rejecting it removes the text. For a deletion it is the other way round.

Wholesale, on the document or on one paragraph — each returns how many revisions it handled:

document.accept_all_revisions()   # -> 12
document.reject_all_revisions()

paragraph.accept_all_revisions()
paragraph.reject_all_revisions()

Warning

Accepting or rejecting mutates the tree, so a list of revisions taken beforehand goes stale. Re-read Document.revisions rather than holding onto revisions across a change, and iterate over a snapshot — as in the loop above, which reads the list once — rather than over a live traversal.

Turning tracking on

Settings.track_revisions is the document-wide flag Word's "Track Changes" button sets:

document.settings.track_revisions = True
document.save("for-review.docx")

This asks Word to record changes made from now on. It does not cause edits made through this library to be recorded as revisions — those are written directly, as if tracking were off.