|
| 1 | +# apply_diff |
| 2 | + |
| 3 | +The `apply_diff` tool makes precise, surgical changes to files by specifying exactly what content to replace. It uses multiple sophisticated strategies for finding and applying changes while maintaining proper code formatting and structure. |
| 4 | + |
| 5 | +## Parameters |
| 6 | + |
| 7 | +The tool accepts these parameters: |
| 8 | + |
| 9 | +- `path` (required): The path of the file to modify relative to the current working directory |
| 10 | +- `diff` (required): The search/replace block defining the changes using a format specific to the active diff strategy |
| 11 | +- `start_line` (optional): A hint for where the search content begins, used by some strategies |
| 12 | +- `end_line` (optional): A hint for where the search content ends, used by some strategies |
| 13 | + |
| 14 | +## What It Does |
| 15 | + |
| 16 | +This tool applies targeted changes to existing files using multiple sophisticated strategies to locate and replace content. Unlike simple search and replace, it implements intelligent matching algorithms that can adapt to different content types and file sizes, with fallback mechanisms for complex changes. |
| 17 | + |
| 18 | +## When is it used? |
| 19 | + |
| 20 | +- When Roo needs to make precise changes to existing code without rewriting entire files |
| 21 | +- When refactoring specific sections of code while maintaining surrounding context |
| 22 | +- When fixing bugs in existing code with surgical precision |
| 23 | +- When implementing feature enhancements that modify only certain parts of a file |
| 24 | + |
| 25 | +## Key Features |
| 26 | + |
| 27 | +- Implements multiple diff strategies optimized for different use cases |
| 28 | +- Uses intelligent fuzzy matching with adaptive confidence thresholds |
| 29 | +- Supports both exact matching and similarity-based matching |
| 30 | +- Preserves code formatting, indentation, and structure |
| 31 | +- Includes git-based fallback mechanism for complex changes |
| 32 | +- Shows changes in a diff view for user approval before applying |
| 33 | +- Adapts matching algorithms based on file size and content uniqueness |
| 34 | +- Provides comprehensive error reporting with context and debugging information |
| 35 | + |
| 36 | +## Limitations |
| 37 | + |
| 38 | +- Works best with unique, distinctive code sections that can be reliably identified |
| 39 | +- Performance may vary with very large files or repetitive code patterns |
| 40 | +- When using fuzzy matching, may occasionally select incorrect matching locations |
| 41 | +- Each diff strategy has different requirements and syntax for specifying changes |
| 42 | +- Some complex edits may require trying multiple strategies or formats |
| 43 | + |
| 44 | +## How It Works |
| 45 | + |
| 46 | +When the `apply_diff` tool is invoked, it follows this process: |
| 47 | + |
| 48 | +1. **Parameter Validation**: Validates the required `path` and `diff` parameters |
| 49 | +2. **Strategy Selection**: Determines which diff strategy to use based on the diff format and configuration |
| 50 | +3. **File Analysis**: Loads the target file's content and analyzes code structure and patterns |
| 51 | +4. **Match Finding**: |
| 52 | + - Uses multiple matching algorithms ranging from exact matches to fuzzy similarity |
| 53 | + - Adapts confidence thresholds based on file size and content uniqueness |
| 54 | + - Uses line numbers as hints but can find matches without them |
| 55 | +5. **Change Preparation**: |
| 56 | + - Shows changes in a diff view before applying them |
| 57 | + - Maintains proper indentation and formatting |
| 58 | +6. **User Approval**: Waits for user approval before applying changes |
| 59 | +7. **Change Application**: Applies approved changes to the file |
| 60 | +8. **Validation**: Confirms the changes were applied correctly |
| 61 | + |
| 62 | +## Diff Strategies |
| 63 | + |
| 64 | +The tool implements several different strategies for applying changes: |
| 65 | + |
| 66 | +### UnifiedDiffStrategy |
| 67 | +Uses standard unified diff format similar to `git diff` output. This is the most traditional diff format used by many tools. |
| 68 | + |
| 69 | +Example format: |
| 70 | +``` |
| 71 | +--- src/utils.ts |
| 72 | ++++ src/utils.ts |
| 73 | +@@ -1,9 +1,10 @@ |
| 74 | + import { Logger } from '../logger'; |
| 75 | + |
| 76 | + function calculateTotal(items: number[]): number { |
| 77 | +- return items.reduce((sum, item) => { |
| 78 | +- return sum + item; |
| 79 | ++ const total = items.reduce((sum, item) => { |
| 80 | ++ return sum + item * 1.1; // Add 10% markup |
| 81 | + }, 0); |
| 82 | ++ return Math.round(total * 100) / 100; // Round to 2 decimal places |
| 83 | + } |
| 84 | +``` |
| 85 | + |
| 86 | +### SearchReplaceDiffStrategy |
| 87 | +A simpler strategy that uses a search/replace block format without line numbers. Useful for direct replacements where you know the exact content to change. |
| 88 | + |
| 89 | +Example format: |
| 90 | +``` |
| 91 | +<<<<<<< SEARCH |
| 92 | +def calculate_total(items): |
| 93 | + total = 0 |
| 94 | + for item in items: |
| 95 | + total += item |
| 96 | + return total |
| 97 | +======= |
| 98 | +def calculate_total(items): |
| 99 | + """Calculate total with 10% markup""" |
| 100 | + return sum(item * 1.1 for item in items) |
| 101 | +>>>>>>> REPLACE |
| 102 | +``` |
| 103 | + |
| 104 | +### MultiSearchReplaceDiffStrategy |
| 105 | +An enhanced version of the search/replace strategy that supports multiple search/replace blocks and includes line number hints to help with locating the content. |
| 106 | + |
| 107 | +Example format: |
| 108 | +``` |
| 109 | +<<<<<<< SEARCH |
| 110 | +:start_line:1 |
| 111 | +:end_line:5 |
| 112 | +------- |
| 113 | +def calculate_total(items): |
| 114 | + total = 0 |
| 115 | + for item in items: |
| 116 | + total += item |
| 117 | + return total |
| 118 | +======= |
| 119 | +def calculate_total(items): |
| 120 | + """Calculate total with 10% markup""" |
| 121 | + return sum(item * 1.1 for item in items) |
| 122 | +>>>>>>> REPLACE |
| 123 | +``` |
| 124 | + |
| 125 | +### NewUnifiedDiffStrategy |
| 126 | +The most sophisticated strategy that implements multiple matching algorithms, fuzzy matching with confidence scoring, and even a git-based fallback mechanism for complex changes. |
| 127 | + |
| 128 | +## Matching Algorithms |
| 129 | + |
| 130 | +The system uses several techniques to find the right location for changes: |
| 131 | + |
| 132 | +### Exact Matching |
| 133 | +When possible, the system tries to find exact matches of the search content in the file. This is the most reliable method when the content hasn't changed. |
| 134 | + |
| 135 | +### Fuzzy Matching |
| 136 | +For cases where the content might have changed slightly, the system uses: |
| 137 | +- Levenshtein distance calculations |
| 138 | +- String similarity comparisons |
| 139 | +- Configurable similarity thresholds |
| 140 | +- Context evaluation for surrounding lines |
| 141 | + |
| 142 | +### Adaptive Thresholds |
| 143 | +The system adjusts matching thresholds based on: |
| 144 | +- File size: Larger files use more relaxed thresholds |
| 145 | +- Content uniqueness: More unique content can use stricter thresholds |
| 146 | +- Pattern complexity: More complex patterns can use more relaxed thresholds |
| 147 | + |
| 148 | +### Content Uniqueness Detection |
| 149 | +The system evaluates how unique the search pattern is within the file to boost confidence in matches: |
| 150 | +```javascript |
| 151 | +// Conceptual example (actual implementation may vary) |
| 152 | +function evaluateContentUniqueness(searchStr: string, content: string[]): number { |
| 153 | + const searchLines = searchStr.split("\n") |
| 154 | + const uniqueLines = new Set(searchLines) |
| 155 | + |
| 156 | + // Calculate how many search lines are relatively unique in the content |
| 157 | + let uniqueCount = 0 |
| 158 | + for (const line of uniqueLines) { |
| 159 | + const matches = contentStr.match(regex) |
| 160 | + if (matches && matches.length <= 2) { |
| 161 | + // Line appears at most twice |
| 162 | + uniqueCount++ |
| 163 | + } |
| 164 | + } |
| 165 | + |
| 166 | + return uniqueCount / uniqueLines.size |
| 167 | +} |
| 168 | +``` |
| 169 | + |
| 170 | +### Git Fallback |
| 171 | +For especially complex changes, the system can create a temporary git repository and use git's own merge algorithms as a last resort: |
| 172 | +```javascript |
| 173 | +// Conceptual example (actual implementation may vary) |
| 174 | +async function applyGitFallback(hunk: Hunk, content: string[]) { |
| 175 | + // Creates temporary git repository |
| 176 | + // Commits original content |
| 177 | + // Commits desired changes |
| 178 | + // Uses git cherry-pick to apply changes |
| 179 | + // Returns result |
| 180 | +} |
| 181 | +``` |
| 182 | + |
| 183 | +## Examples When Used |
| 184 | + |
| 185 | +- When fixing a bug, Roo identifies the specific function containing the bug and uses `apply_diff` to modify only that function, using fuzzy matching if the function has been slightly modified. |
| 186 | +- When refactoring code, Roo uses `apply_diff` with the appropriate strategy based on how extensive the changes are. |
| 187 | +- When implementing a feature enhancement, Roo adds new capabilities to existing functions using targeted edits, with git fallback for complex changes. |
| 188 | +- When updating API usage across a codebase, Roo identifies all instances of deprecated calls and replaces them with updated versions, adapting confidence thresholds based on pattern uniqueness. |
| 189 | + |
| 190 | +## Usage Examples |
| 191 | + |
| 192 | +Using the MultiSearchReplaceDiffStrategy: |
| 193 | +``` |
| 194 | +<apply_diff> |
| 195 | +<path>src/calculation.py</path> |
| 196 | +<diff> |
| 197 | +<<<<<<< SEARCH |
| 198 | +:start_line:1 |
| 199 | +:end_line:5 |
| 200 | +------- |
| 201 | +def calculate_total(items): |
| 202 | + total = 0 |
| 203 | + for item in items: |
| 204 | + total += item |
| 205 | + return total |
| 206 | +======= |
| 207 | +def calculate_total(items): |
| 208 | + """Calculate total with 10% markup""" |
| 209 | + return sum(item * 1.1 for item in items) |
| 210 | +>>>>>>> REPLACE |
| 211 | +</diff> |
| 212 | +</apply_diff> |
| 213 | +``` |
| 214 | + |
| 215 | +Using the UnifiedDiffStrategy: |
| 216 | +``` |
| 217 | +<apply_diff> |
| 218 | +<path>src/utils.ts</path> |
| 219 | +<diff> |
| 220 | +--- src/utils.ts |
| 221 | ++++ src/utils.ts |
| 222 | +@@ -24,7 +24,7 @@ |
| 223 | + function formatUsername(name: string): string { |
| 224 | + return name |
| 225 | + .toLowerCase() |
| 226 | +- .replace(/[^a-z0-9]/g, ''); |
| 227 | ++ .replace(/[^a-z0-9_-]/g, ''); |
| 228 | + } |
| 229 | +</diff> |
| 230 | +</apply_diff> |
| 231 | +``` |
| 232 | + |
| 233 | +Using the SearchReplaceDiffStrategy for simple replacements: |
| 234 | +``` |
| 235 | +<apply_diff> |
| 236 | +<path>src/cart.js</path> |
| 237 | +<diff> |
| 238 | +<<<<<<< SEARCH |
| 239 | +function calculateDiscount(price, quantity) { |
| 240 | + let discount = 0; |
| 241 | + if (quantity > 10) { |
| 242 | + discount = price * 0.1; |
| 243 | + } |
| 244 | + return discount; |
| 245 | +} |
| 246 | +======= |
| 247 | +function calculateDiscount(price, quantity) { |
| 248 | + let discount = 0; |
| 249 | + if (quantity > 10) { |
| 250 | + discount = price * quantity * 0.1; |
| 251 | + } |
| 252 | + return discount; |
| 253 | +} |
| 254 | +>>>>>>> REPLACE |
| 255 | +</diff> |
| 256 | +</apply_diff> |
| 257 | +``` |
0 commit comments