How thefuck Handles Conflicting Rules and Multiple Corrections
When multiple rules match a failed command, thefuck merges all suggestions, removes duplicates based on script content, and selects the highest-priority correction as the default.
The open-source tool thefuck (available at nvbn/thefuck) automatically corrects mistyped console commands by applying a library of user-contributed rules. When a command fails and several rules could potentially fix it, the framework must resolve these conflicts deterministically. Understanding how thefuck handles conflicting rules reveals a sophisticated priority-based arbitration system that ensures users receive the most relevant suggestion first.
How thefuck Loads and Prioritizes Rules
Before any conflict resolution occurs, thefuck assembles its rule set from multiple sources and assigns each rule a priority value.
Rule Discovery and Priority Assignment
In thefuck/corrector.py, the get_rules() function discovers rules from three locations:
- The bundled
thefuck/rulesdirectory - The user's
~/.config/thefuck/rulesfolder - Any installed third-party packages matching
thefuck_contrib_*
Each rule is assigned a priority value. By default, rules use DEFAULT_PRIORITY, but developers or users can override this. The rules are then sorted by this priority value, with lower numbers indicating higher precedence (i.e., a rule with priority 100 is evaluated before a rule with priority 200).
# From thefuck/corrector.py
def get_rules():
# Loads rules and sorts by priority
rules = sorted(_get_all_rules(), key=lambda rule: rule.priority)
return rules
Collecting and Matching Candidate Corrections
When you invoke fuck after a failed command, thefuck enters the matching phase to identify which rules apply to your specific error.
The Matching Process
The entry point get_corrected_commands() in thefuck/corrector.py iterates through the priority-sorted rule list. For each rule, it performs two checks:
- Match validation: Calls
Rule.is_match(command)to determine if the rule applies to the failed command - Correction generation: If matched, calls
Rule.get_corrected_commands(command)to retrieve suggested fixes
This process yields a flat generator of CorrectedCommand objects from all matching rules, not just the first one found.
# Conceptual flow from thefuck/corrector.py
def get_corrected_commands(command, rules):
for rule in rules:
if rule.is_match(command):
yield from rule.get_corrected_commands(command)
Generating CorrectedCommand Objects
Each CorrectedCommand object encapsulates the suggested script and its associated priority. In thefuck/types.py, the Rule.get_corrected_commands() method calculates an effective priority for each suggestion using the formula (n + 1) * rule.priority, where n is the suggestion's index from that specific rule. This ensures that even within a single rule's multiple suggestions, ordering is preserved while maintaining the rule's base priority weight.
Resolving Conflicts Through Deduplication and Sorting
Once all matching rules have contributed their suggestions, thefuck must resolve conflicts where different rules propose the same fix, and establish a deterministic order for presentation.
The organize_commands() Function
The organize_commands() function in thefuck/corrector.py implements the core conflict resolution logic:
- First command preservation: It immediately extracts the first command from the generator (which comes from the highest-priority rule) and reserves it as the primary suggestion
- Deduplication: It collects remaining commands into a set, using
CorrectedCommand.__eq__for comparison - Priority sorting: It sorts the unique commands by their effective priority
# From thefuck/corrector.py
def organize_commands(corrected_commands):
# First command kept separate (highest priority)
first_command = next(corrected_commands)
# Deduplicate remaining commands
unique_commands = set(corrected_commands)
# Sort by effective priority
sorted_commands = sorted(unique_commands, key=lambda cmd: cmd.priority)
return first_command, sorted_commands
Deduplication Logic
In thefuck/types.py, the CorrectedCommand class implements __eq__ to compare commands based solely on their script content, ignoring the priority field. This means if two different rules suggest the exact same shell command (e.g., both suggesting git status for a mistyped git stats), they are considered duplicates and only one instance appears in the final list.
# From thefuck/types.py
class CorrectedCommand:
def __eq__(self, other):
# Equality based on script content only, not priority
return self.script == other.script
Priority-Based Sorting
After deduplication, commands are sorted by their effective priority value. Since lower priority numbers indicate higher precedence, the command with the smallest priority value appears first. This deterministic sorting ensures consistent behavior across invocations and allows high-priority rules (like safety-critical corrections) to override generic suggestions.
User Configuration of Rule Precedence
Users can influence how thefuck handles conflicts through environment variables and configuration files.
In thefuck/conf.py, the Settings class parses the THEFUCK_PRIORITY environment variable and the ~/.config/thefuck/settings.py file. These values populate settings.priority, which Rule.from_path() in thefuck/types.py consumes when instantiating rules. By setting custom priority values, users can elevate or demote specific rules to ensure their preferred corrections win in conflict scenarios.
# Example: Prioritize git suggestions over system commands
export THEFUCK_PRIORITY="git_commit_amend=50,apt_get=900"
Summary
- thefuck resolves conflicting rules by loading all enabled rules, sorting them by priority, and collecting suggestions from every matching rule.
- The
organize_commands()function inthefuck/corrector.pyhandles deduplication by comparing script content only, ensuring identical suggestions from different rules collapse into one entry. - Corrections are sorted by effective priority, with lower values indicating higher precedence, ensuring deterministic selection of the best fix.
- Users can override rule priorities via the
THEFUCK_PRIORITYenvironment variable orsettings.pyto customize conflict resolution behavior.
Frequently Asked Questions
How does thefuck decide which correction to show first when multiple rules match?
When multiple rules match a failed command, thefuck collects all suggestions and sorts them by effective priority. The rule with the lowest priority value (highest precedence) wins. The organize_commands() function in thefuck/corrector.py ensures the first command in the sorted list is presented as the default correction.
Can two different rules suggest the same correction, and how does thefuck handle this?
Yes, different rules can suggest identical shell commands. Thefuck handles this through deduplication in CorrectedCommand.__eq__ (defined in thefuck/types.py), which compares commands based on script content while ignoring priority. When organize_commands() processes suggestions, it stores them in a set, automatically collapsing duplicates into a single entry.
How can I customize which rules take precedence when conflicts occur?
You can customize rule precedence using the THEFUCK_PRIORITY environment variable or by editing ~/.config/thefuck/settings.py. These settings allow you to assign custom priority values to specific rules. Lower values indicate higher precedence, so setting a rule to priority 50 ensures it overrides rules with default priorities of 900 or 1000.
What happens if no rules match a failed command?
If no rules match, get_corrected_commands() in thefuck/corrector.py yields an empty generator. The entry point fix_command detects this empty result and typically exits without suggesting a correction, allowing the user to try a different approach or manually fix the command.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →