History and Change Tracking
Relevant source files
The following files were used as context for generating this wiki page:
The History and Change Tracking system in Rope provides a robust framework for recording, applying, and reversing modifications to a Python project's resources. Every refactoring operation in Rope generates Change objects that can be previewed, committed to the filesystem, or undone. This system ensures safety during complex code transformations by maintaining a linear history of states and handling dependencies between individual file changes.
At its core, the system manages a stack of ChangeSet objects, allowing developers to roll back to previous versions of the codebase or redo changes that were recently reversed. It integrates directly with the project's filesystem commands and resource observers to ensure that all modifications are tracked accurately across files and folders.
Change Management Architecture
The system is built on a hierarchy of classes representing different types of filesystem modifications. The base class Change defines the interface for performing (do) and reversing (undo) actions.
Core Change Types
| Class | Description | Key Fields |
|---|---|---|
ChangeContents | Modifies the text content of a specific file. | resource, new_contents, old_contents |
MoveResource | Renames or moves a file or folder to a new location. | resource, new_resource |
CreateResource | Creates a new file or folder. | resource |
RemoveResource | Deletes a file or folder from the filesystem. | resource |
ChangeSet | A collection of multiple changes applied as a single atomic unit. | changes, description, time |
Sources: rope/base/change.py:12-258
Atomic Operations and Descriptions
Refactorings typically return a ChangeSet, which groups related changes. For example, a global move refactoring might include both a MoveResource for the file itself and multiple ChangeContents operations for updating imports in other modules.
The diagram above illustrates the inheritance structure where specific filesystem actions inherit from the base Change class, and ChangeSet acts as a composite.
Sources: rope/base/change.py:12-220
Project History Workflow
The History class maintains two lists: undo_list and redo_list. When a change is performed via project.do(), it is passed to the history manager, which executes the change and stores it in the undo_list.
Execution Logic
- Perform Change: The
do()method is called on theChangeobject. - Interest Check: The history manager checks if the change affects any non-ignored resources.
- Record: If interesting, the change is appended to
undo_list. - Redo Clear: The
redo_listis cleared whenever a new "do" operation occurs to maintain a linear timeline.
Sources: rope/base/history.py:27-50, rope/base/project.py:101-106
Dependency Handling
When undoing a specific change that is not the most recent, Rope automatically identifies and undoes all subsequent changes that depend on it. Dependencies are determined by checking if subsequent changes affect the same resources or child resources of the original change.
This flowchart describes the process of managing dependencies during an undo operation to ensure filesystem consistency.
Sources: rope/base/history.py:64-90, rope/base/history.py:168-191
Serialization and Persistence
Rope can persist its history across sessions if the save_history preference is enabled. This is managed through ChangeToData and DataToChange converters, which translate complex Change objects into serializable formats (Pickle and JSON).
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
save_history | boolean | False | Whether to persist history to the .ropeproject folder. |
max_history_items | integer | 100 | The maximum number of undo steps to retain in history. |
Sources: rope/base/history.py:147-158, rope/base/history.py:237-252, rope/base/project.py:255-274
Data Transformation Flow
The transformation involves converting resource objects into relative paths and serializing the new and old contents for ChangeContents operations.
# Example of how ChangeContents is serialized for persistence
def convertChangeContents(self, change):
return (change.resource.path, change.new_contents, change.old_contents)
Sources: rope/base/change.py:382-383
Summary
The History and Change Tracking system provides the foundational safety net for Python refactoring. By encapsulating every filesystem modification as a reversible Change object and managing a dependency-aware History stack, it allows for complex multi-file restructurings while maintaining the ability to revert to a stable state. The integration with the .ropeproject data store further enables long-running refactoring tasks to persist across development sessions.