1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-11-01 15:42:06 +00:00

Compare commits

...

36 Commits
3.31 ... 3.32

Author SHA1 Message Date
Pablo Santiago Blum de Aguiar
841e3f9e13 Bump to 3.32 2022-01-02 22:45:09 +01:00
Joris Hartog
0f4a523dc4 Encapsulate force_command in _get_raw_command
Using the `force_command` argument will run into issues as the
`_get_raw_command` method simply returns the value of `force_command`
(which is a string) while it should actually return a list.

Fix #1240
2021-12-20 00:05:47 +01:00
Benjamin Rood
c719712b62 Use --user with pip, not sudo pip
`sudo` with `pip` is [considered unsafe](https://stackoverflow.com/a/22517157/2469559).

Instead, run such commands with the `--user` argument to get the same result _safely._
2021-09-05 16:58:35 +02:00
Pablo Santiago Blum de Aguiar
51e4e87280 #1227: Make git_support support output-independent rules
Fix #1227
2021-08-17 15:41:54 +02:00
Pablo Santiago Blum de Aguiar
7b7c150bb7 #697: Encode expanded script on Python 2
Fix #697
2021-08-17 15:40:56 +02:00
Yotam Salmon
2a166a7dec #977: Add wrong_hyphen_before_subcommand rule
Co-authored-by: user <avi.salmon@intel.com>
Co-authored-by: Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
2021-08-07 23:26:36 +02:00
Lars Alexander Blumberg
8fa10b1049 #868: Fix outdated link to homebrew for Linux (#1226) 2021-08-07 13:12:12 +02:00
Pablo Santiago Blum de Aguiar
7f3442747e #579: Ignore commands of len 1 in missing_space_before_subcommand 2021-08-01 22:24:29 +02:00
Pablo Santiago Blum de Aguiar
8bebce331e #933: Correctly redefine the function with a cache 2021-08-01 22:07:19 +02:00
Pablo Santiago Blum de Aguiar
dbc435c040 #618: Fix git_push_without_commits rule
The rule was in a non-working state. And the tests needed some bit of
simplification. Certain degree of repetition is oftentimes a good thing.
2021-07-29 22:11:34 +02:00
Pablo Santiago Blum de Aguiar
30c90bccaa #1184: Configure devcontainer shell with recommended way
https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration
2021-07-29 22:09:21 +02:00
Pablo Santiago Blum de Aguiar
8e8c80c227 #1188: Remove only leading whitespace chars from script
Fix #1188
2021-07-29 21:43:24 +02:00
Pablo Santiago Blum de Aguiar
c2df71caed #994: Replace decoding errors with the replacement marker
Fix #994

Reference: https://docs.python.org/3/library/codecs.html#error-handlers
2021-07-29 21:37:19 +02:00
Pablo Santiago Blum de Aguiar
eb05b28c5b #N/A: Replace only exact words when expanding a Git alias 2021-07-29 21:36:15 +02:00
Divy Jain
a2a6cbdc70 #894: Combine commands with shell.and_ in docker_login rule 2021-07-29 13:49:42 +02:00
Pablo Santiago Blum de Aguiar
58ddd4338a #1215: Initiate settings before printing the alias 2021-07-21 13:09:19 +02:00
Dave Storey
58f61d8090 #1184: Add devcontainer for easy Python development
* adding devcontainer and new rule for main-master

* update devcontainer work

* cleaning up devcontainer.json

* fixed line ending

* move to VARIANT 3

* Update .devcontainer/devcontainer.json

* Update .devcontainer/devcontainer.json

* Update .devcontainer/devcontainer.json

Co-authored-by: Pablo Aguiar <scorphus@gmail.com>
2021-07-19 22:47:19 +02:00
Pablo Santiago Blum de Aguiar
0668822abb #1215: Remove redirection to stderr with the ^ character
Redirection to standard error with the `^` character is disabled by
default since Fish Shell version 3.3[1].

Fix #1214

[1]: https://github.com/fish-shell/fish-shell/blob/master/CHANGELOG.rst#deprecations-and-removed-features-1
2021-07-18 15:10:48 +02:00
Pablo Santiago Blum de Aguiar
711feb4df5 #1184: Improve + fix git_main_master rule 2021-07-09 16:13:25 +02:00
Dave Storey
70a42b54ab #1184: Add new rule for main / master Git branches 2021-04-06 14:50:43 +01:00
Pablo Santiago Blum de Aguiar
11b70526f7 #1131: Improve git_commit_add rule
Add more capabilities to the rule, remove its priority and fix tests
2021-07-08 21:43:35 +02:00
Sergei Haller
55922e4dbe #1131: Add rule for Git commit with no added files 2020-09-08 11:47:05 +02:00
Pablo Santiago Blum de Aguiar
799f4127ca #942: Improve git_branch_0flag rule - with a new name 2021-07-13 15:36:11 +02:00
Ryan Delaney
fe1942866b #1133: Match commands with path prefixes in @for_app decorations
* Resolve paths before checking app identity

Commands entered with a path do not match is_app. I encountered this
when working with a test for the rm_dir rule. This rule did not use the
@for_app decorator, but when I migrated it, the test for "./bin/hdfs.."
failed because 'hdfs' was recognized as a command, while "./bin/hdfs"
was not.

This commit addresses the false negative by resolving path names in the
command, via os.path.basename.

* Remove paths from for_app invocations in rules

I presume that the `./` in `./gradlew` was used here because thefuck
would not find an app match on just `gradlew`, and thus no fucks would
be given on the most common and idiomatic way of invoking gradlew.

After 8faf9b1, thefuck does not distinguish between commands with
paths and those without. Therefore, the tests for this rule are now
broken because thefuck strips paths from the _user_'s command, but not
from the for_app decoration.

This commit addresses that problem by changing the for_app decoration to
this rule.
2021-07-07 23:05:55 +02:00
M. H. Kwon
13fda64d6e #1109: Fix a typo on a comment
on -> one
2021-07-06 16:58:44 +02:00
Abraham Chan
6111523034 #1210: Add rule 'rails_migrations_pending'
* Add rule 'rails_migrations_pending'

* Update thefuck/rules/rails_migrations_pending.py

Co-authored-by: Pablo Aguiar <scorphus@gmail.com>

* Add initial command to corrected command

Co-authored-by: Pablo Aguiar <scorphus@gmail.com>
2021-07-06 12:52:54 +02:00
ProfessorTom
24576b30b2 #942: Add new git_branch_0v_to_dash_v rule
* fix fuckup `branch 0v` by...

...deleting branch `0v` and running `git branch -v` as the user intended

* use quotes consistently

* provide new  solution implementation based on feedback on PR

* rename files to more accurately reflect their more generic contents

* update import statement to match new file name

* update command name in README.md

* separate out matching tests so the pattern is clear for those who add matches in the future
2021-07-01 23:07:38 +02:00
T.J. Kolberg
373f445e9b #1150: Update the name of macOS on README
* Update README.md

Co-authored-by: Pablo Aguiar <scorphus@gmail.com>
2021-07-01 11:47:12 +02:00
Divy Jain
54253027e3 #1123: Update composer_not_command rule (#1135)
Fix #1123
2021-06-30 23:10:06 +02:00
theslimshaney
9201ce79cf #1039: Remove all leading $ not just one 2021-06-30 22:46:20 +02:00
Romain Heller
7f97818663 #455: [README] Add uninstall instructions (#1171)
* ~[Doc] Add Uninstall instructions

As we need the package and to modify the shell config, users could have a pain in the ass when it comes to retire *The Fuck* from the system.

* Update README.md

* Update README.md

Co-authored-by: Pablo Aguiar <scorphus@gmail.com>
2021-06-30 14:20:48 +02:00
Abraham Chan
2f4adcf3cb #N/A: Fix yield_fixture deprecation (#1211) 2021-06-29 21:26:13 +02:00
Elisha Hollander
06e14afd17 #N/A: Fix grammar and spelling errors (#1193)
* Fix grammar and spelling errors

* Update README.md

Co-authored-by: Pablo Aguiar <scorphus@gmail.com>

Co-authored-by: Pablo Aguiar <scorphus@gmail.com>
2021-06-29 21:25:01 +02:00
Utkarsh Agarwal
3651b0fa0c #1164: Optimize GIFs with ImgBot
*Total -- 1,239.63kb -> 1,088.54kb (12.19%)

/example_instant_mode.gif -- 535.22kb -> 457.41kb (14.54%)
/example.gif -- 704.41kb -> 631.13kb (10.4%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>

Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
2021-06-23 12:19:47 +02:00
Felix Yan
d723bb71b7 Avoid using pkg_resources
pkg_resources is expensive to load, and it's an external module provided
by the setuptools project.

Let's use importlib.metadata from Python 3.8+'s stdlib when available, which
is much faster. This also erases setuptools from thefuck's runtime
dependency.

Simple benchmark data for `thefuck --version`:

Before: 0.602s
After: 0.169s
2021-06-21 17:06:02 +02:00
Hamid Nazari
b2e1886de8 Fix git_hook_bypass priority (#1207) 2021-06-21 16:32:35 +02:00
44 changed files with 568 additions and 86 deletions

10
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1,10 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.163.1/containers/python-3/.devcontainer/base.Dockerfile
# [Choice] Python version: 3, 3.9, 3.8, 3.7, 3.6
ARG VARIANT="3"
FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}
# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image.
COPY requirements.txt /tmp/pip-tmp/
RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \
&& rm -rf /tmp/pip-tmp

View File

@@ -0,0 +1,39 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.163.1/containers/python-3
{
"name": "Python 3",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
// Set *default* container specific settings.json values on container create.
"settings": {
"terminal.integrated.profiles.linux": {
"bash (login)": {
"path": "bash",
"args": ["-l"]
}
},
"python.pythonPath": "/usr/local/bin/python",
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint"
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"ms-python.python"
],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "pip3 install -r requirements.txt && python3 setup.py develop"
}

View File

@@ -26,6 +26,13 @@ fixes, etc.
# Developing
In order to develop locally, there are two options:
- Develop using a local installation of Python 3 and setting up a virtual environment
- Develop using an automated VSCode Dev Container.
## Develop using local Python installation
[Create and activate a Python 3 virtual environment.](https://docs.python.org/3/tutorial/venv.html)
Install `The Fuck` for development:
@@ -59,3 +66,27 @@ For sending package to pypi:
sudo apt-get install pandoc
./release.py
```
## Develop using Dev Container
To make local development easier a [VSCode Devcontainer](https://code.visualstudio.com/docs/remote/remote-overview) is included with this repository. This will allows you to spin up a Docker container with all the necessary prerequisites for this project pre-installed ready to go, no local Python install/setup required.
### Prerequisites
To use the container you require:
- [Docker](https://www.docker.com/products/docker-desktop)
- [VSCode](https://code.visualstudio.com/)
- [VSCode Remote Development Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack)
- [Windows Users Only]: [Installation of WSL2 and configuration of Docker to use it](https://docs.docker.com/docker-for-windows/wsl/)
Full notes about [installation are here](https://code.visualstudio.com/docs/remote/containers#_installation)
### Running the container
Assuming you have the prerequisites:
1. Open VSCode
1. Open command palette (CMD+SHIFT+P (mac) or CTRL+SHIFT+P (windows))
1. Select `Remote-Containers: Reopen in Container`.
1. Container will be built, install all pip requirements and your VSCode will mount into it automagically.
1. Your VSCode and container now essentially become a throw away environment.

View File

@@ -114,7 +114,7 @@ Reading package lists... Done
## Installation
On OS X, you can install *The Fuck* via [Homebrew][homebrew] (or via [Linuxbrew][linuxbrew] on Linux):
On macOS or Linux, you can install *The Fuck* via [Homebrew][homebrew]:
```bash
brew install thefuck
@@ -124,7 +124,7 @@ On Ubuntu / Mint, install *The Fuck* with the following commands:
```bash
sudo apt update
sudo apt install python3-dev python3-pip python3-setuptools
sudo pip3 install thefuck
pip3 install thefuck --user
```
On FreeBSD, install *The Fuck* with the following commands:
@@ -182,6 +182,12 @@ pip3 install thefuck --upgrade
**Note: Alias functionality was changed in v1.34 of *The Fuck***
## Uninstall
To remove *The Fuck*, reverse the installation process:
- erase or comment *thefuck* alias line from your Bash, Zsh, Fish, Powershell, tcsh, ... shell config
- use your package manager (brew, pip3, pkg, crew, pip) to uninstall the binaries
## How it works
*The Fuck* attempts to match the previous command with a rule. If a match is
@@ -225,8 +231,10 @@ following rules are enabled by default:
* `git_branch_delete_checked_out` &ndash; changes `git branch -d` to `git checkout master && git branch -D` when trying to delete a checked out branch;
* `git_branch_exists` &ndash; offers `git branch -d foo`, `git branch -D foo` or `git checkout foo` when creating a branch that already exists;
* `git_branch_list` &ndash; catches `git branch list` in place of `git branch` and removes created branch;
* `git_branch_0flag` &ndash; fixes commands such as `git branch 0v` and `git branch 0r` removing the created branch;
* `git_checkout` &ndash; fixes branch name or creates new branch;
* `git_clone_git_clone` &ndash; replaces `git clone git clone ...` with `git clone ...`
* `git_commit_add` &ndash; offers `git commit -a ...` or `git commit -p ...` after previous commit if it failed because nothing was staged;
* `git_commit_amend` &ndash; offers `git commit --amend` after previous commit;
* `git_commit_reset` &ndash; offers `git reset HEAD~` after previous commit;
* `git_diff_no_index` &ndash; adds `--no-index` to previous `git diff` on untracked files;
@@ -236,6 +244,7 @@ following rules are enabled by default:
* `git_help_aliased` &ndash; fixes `git help <alias>` commands replacing <alias> with the aliased command;
* `git_hook_bypass` &ndash; adds `--no-verify` flag previous to `git am`, `git commit`, or `git push` command;
* `git_lfs_mistype` &ndash; fixes mistyped `git lfs <command>` commands;
* `git_main_master` &ndash; fixes incorrect branch name between `main` and `master`
* `git_merge` &ndash; adds remote to branch names;
* `git_merge_unrelated` &ndash; adds `--allow-unrelated-histories` when required
* `git_not_command` &ndash; fixes wrong git commands like `git brnch`;
@@ -243,7 +252,7 @@ following rules are enabled by default:
* `git_pull_clone` &ndash; clones instead of pulling when the repo does not exist;
* `git_pull_uncommitted_changes` &ndash; stashes changes before pulling and pops them afterwards;
* `git_push` &ndash; adds `--set-upstream origin $branch` to previous failed `git push`;
* `git_push_different_branch_names` &ndash; fixes pushes when local brach name does not match remote branch name;
* `git_push_different_branch_names` &ndash; fixes pushes when local branch name does not match remote branch name;
* `git_push_pull` &ndash; runs `git pull` when `push` was rejected;
* `git_push_without_commits` &ndash; Creates an initial commit if you forget and only `git add .`, when setting up a new project;
* `git_rebase_no_changes` &ndash; runs `git rebase --skip` instead of `git rebase --continue` when there are no changes;
@@ -268,7 +277,7 @@ following rules are enabled by default:
* `has_exists_script` &ndash; prepends `./` when script/binary exists;
* `heroku_multiple_apps` &ndash; add `--app <app>` to `heroku` commands like `heroku pg`;
* `heroku_not_command` &ndash; fixes wrong `heroku` commands like `heroku log`;
* `history` &ndash; tries to replace command with most similar command from history;
* `history` &ndash; tries to replace command with the most similar command from history;
* `hostscli` &ndash; tries to fix `hostscli` usage;
* `ifconfig_device_not_found` &ndash; fixes wrong device names like `wlan0` to `wlp2s0`;
* `java` &ndash; removes `.java` extension when running Java programs;
@@ -283,7 +292,7 @@ following rules are enabled by default:
* `man_no_space` &ndash; fixes man commands without spaces, for example `mandiff`;
* `mercurial` &ndash; fixes wrong `hg` commands;
* `missing_space_before_subcommand` &ndash; fixes command with missing space like `npminstall`;
* `mkdir_p` &ndash; adds `-p` when you try to create a directory without parent;
* `mkdir_p` &ndash; adds `-p` when you try to create a directory without a parent;
* `mvn_no_command` &ndash; adds `clean package` to `mvn`;
* `mvn_unknown_lifecycle_phase` &ndash; fixes misspelled life cycle phases with `mvn`;
* `npm_missing_script` &ndash; fixes `npm` custom script name in `npm run-script <script>`;
@@ -302,16 +311,17 @@ following rules are enabled by default:
* `python_execute` &ndash; appends missing `.py` when executing Python files;
* `python_module_error` &ndash; fixes ModuleNotFoundError by trying to `pip install` that module;
* `quotation_marks` &ndash; fixes uneven usage of `'` and `"` when containing args';
* `path_from_history` &ndash; replaces not found path with similar absolute path from history;
* `path_from_history` &ndash; replaces not found path with a similar absolute path from history;
* `rails_migrations_pending` &ndash; runs pending migrations;
* `react_native_command_unrecognized` &ndash; fixes unrecognized `react-native` commands;
* `remove_shell_prompt_literal` &ndash; remove leading shell prompt symbol `$`, common when copying commands from documentations;
* `remove_trailing_cedilla` &ndash; remove trailing cedillas `ç`, a common typo for european keyboard layouts;
* `remove_trailing_cedilla` &ndash; remove trailing cedillas `ç`, a common typo for European keyboard layouts;
* `rm_dir` &ndash; adds `-rf` when you try to remove a directory;
* `scm_correction` &ndash; corrects wrong scm like `hg log` to `git log`;
* `sed_unterminated_s` &ndash; adds missing '/' to `sed`'s `s` commands;
* `sl_ls` &ndash; changes `sl` to `ls`;
* `ssh_known_hosts` &ndash; removes host from `known_hosts` on warning;
* `sudo` &ndash; prepends `sudo` to previous command if it failed because of permissions;
* `sudo` &ndash; prepends `sudo` to the previous command if it failed because of permissions;
* `sudo_command_from_user_path` &ndash; runs commands from users `$PATH` with `sudo`;
* `switch_lang` &ndash; switches command from your local layout to en;
* `systemctl` &ndash; correctly orders parameters of confusing `systemctl`;
@@ -322,10 +332,11 @@ following rules are enabled by default:
* `tsuru_not_command` &ndash; fixes wrong `tsuru` commands like `tsuru shell`;
* `tmux` &ndash; fixes `tmux` commands;
* `unknown_command` &ndash; fixes hadoop hdfs-style "unknown command", for example adds missing '-' to the command on `hdfs dfs ls`;
* `unsudo` &ndash; removes `sudo` from previous command if a process refuses to run on super user privilege.
* `unsudo` &ndash; removes `sudo` from previous command if a process refuses to run on superuser privilege.
* `vagrant_up` &ndash; starts up the vagrant instance;
* `whois` &ndash; fixes `whois` command;
* `workon_doesnt_exists` &ndash; fixes `virtualenvwrapper` env name os suggests to create new.
* `wrong_hyphen_before_subcommand` &ndash; removes an improperly placed hyphen (`apt-install` -> `apt install`, `git-log` -> `git log`, etc.)
* `yarn_alias` &ndash; fixes aliased `yarn` commands like `yarn ls`;
* `yarn_command_not_found` &ndash; fixes misspelled `yarn` commands;
* `yarn_command_replaced` &ndash; fixes replaced `yarn` commands;
@@ -425,15 +436,15 @@ Several *The Fuck* parameters can be changed in the file `$XDG_CONFIG_HOME/thefu
* `rules` &ndash; list of enabled rules, by default `thefuck.const.DEFAULT_RULES`;
* `exclude_rules` &ndash; list of disabled rules, by default `[]`;
* `require_confirmation` &ndash; requires confirmation before running new command, by default `True`;
* `wait_command` &ndash; max amount of time in seconds for getting previous command output;
* `wait_command` &ndash; the max amount of time in seconds for getting previous command output;
* `no_colors` &ndash; disable colored output;
* `priority` &ndash; dict with rules priorities, rule with lower `priority` will be matched first;
* `debug` &ndash; enables debug output, by default `False`;
* `history_limit` &ndash; numeric value of how many history commands will be scanned, like `2000`;
* `history_limit` &ndash; the numeric value of how many history commands will be scanned, like `2000`;
* `alter_history` &ndash; push fixed command to history, by default `True`;
* `wait_slow_command` &ndash; max amount of time in seconds for getting previous command output if it in `slow_commands` list;
* `slow_commands` &ndash; list of slow commands;
* `num_close_matches` &ndash; maximum number of close matches to suggest, by default `3`.
* `num_close_matches` &ndash; the maximum number of close matches to suggest, by default `3`.
* `excluded_search_path_prefixes` &ndash; path prefixes to ignore when searching for commands, by default `[]`.
An example of `settings.py`:
@@ -457,16 +468,16 @@ Or via environment variables:
* `THEFUCK_RULES` &ndash; list of enabled rules, like `DEFAULT_RULES:rm_root` or `sudo:no_command`;
* `THEFUCK_EXCLUDE_RULES` &ndash; list of disabled rules, like `git_pull:git_push`;
* `THEFUCK_REQUIRE_CONFIRMATION` &ndash; require confirmation before running new command, `true/false`;
* `THEFUCK_WAIT_COMMAND` &ndash; max amount of time in seconds for getting previous command output;
* `THEFUCK_WAIT_COMMAND` &ndash; the max amount of time in seconds for getting previous command output;
* `THEFUCK_NO_COLORS` &ndash; disable colored output, `true/false`;
* `THEFUCK_PRIORITY` &ndash; priority of the rules, like `no_command=9999:apt_get=100`,
rule with lower `priority` will be matched first;
* `THEFUCK_DEBUG` &ndash; enables debug output, `true/false`;
* `THEFUCK_HISTORY_LIMIT` &ndash; how many history commands will be scanned, like `2000`;
* `THEFUCK_ALTER_HISTORY` &ndash; push fixed command to history `true/false`;
* `THEFUCK_WAIT_SLOW_COMMAND` &ndash; max amount of time in seconds for getting previous command output if it in `slow_commands` list;
* `THEFUCK_WAIT_SLOW_COMMAND` &ndash; the max amount of time in seconds for getting previous command output if it in `slow_commands` list;
* `THEFUCK_SLOW_COMMANDS` &ndash; list of slow commands, like `lein:gradle`;
* `THEFUCK_NUM_CLOSE_MATCHES` &ndash; maximum number of close matches to suggest, like `5`.
* `THEFUCK_NUM_CLOSE_MATCHES` &ndash; the maximum number of close matches to suggest, like `5`.
* `THEFUCK_EXCLUDED_SEARCH_PATH_PREFIXES` &ndash; path prefixes to ignore when searching for commands, by default `[]`.
For example:
@@ -544,6 +555,5 @@ Project License can be found [here](LICENSE.md).
[examples-link]: https://raw.githubusercontent.com/nvbn/thefuck/master/example.gif
[instant-mode-gif-link]: https://raw.githubusercontent.com/nvbn/thefuck/master/example_instant_mode.gif
[homebrew]: https://brew.sh/
[linuxbrew]: https://linuxbrew.sh/
##### [Back to Contents](#contents)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 KiB

After

Width:  |  Height:  |  Size: 631 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 535 KiB

After

Width:  |  Height:  |  Size: 457 KiB

View File

@@ -31,7 +31,7 @@ elif (3, 0) < version < (3, 5):
' ({}.{} detected).'.format(*version))
sys.exit(-1)
VERSION = '3.31'
VERSION = '3.32'
install_requires = ['psutil', 'colorama', 'six', 'decorator', 'pyte']
extras_require = {':python_version<"3.4"': ['pathlib2'],

View File

@@ -1,6 +1,6 @@
from mock import Mock
import pytest
from thefuck.entrypoints.alias import _get_alias
from thefuck.entrypoints.alias import _get_alias, print_alias
@pytest.mark.parametrize(
@@ -28,3 +28,12 @@ def test_get_alias(monkeypatch, mocker, py2,
assert alias == 'instant_mode_alias'
else:
assert alias == 'app_alias'
def test_print_alias(mocker):
settings_mock = mocker.patch('thefuck.entrypoints.alias.settings')
_get_alias_mock = mocker.patch('thefuck.entrypoints.alias._get_alias')
known_args = Mock()
print_alias(known_args)
settings_mock.init.assert_called_once_with(known_args)
_get_alias_mock.assert_called_once_with(known_args)

View File

@@ -5,8 +5,8 @@ from thefuck.entrypoints.fix_command import _get_raw_command
class TestGetRawCommand(object):
def test_from_force_command_argument(self):
known_args = Mock(force_command=['git', 'brunch'])
assert _get_raw_command(known_args) == ['git', 'brunch']
known_args = Mock(force_command='git brunch')
assert _get_raw_command(known_args) == ['git brunch']
def test_from_command_argument(self, os_environ):
os_environ['TF_HISTORY'] = None

View File

@@ -22,6 +22,19 @@ class TestRerun(object):
assert rerun.get_output('', '') is None
wait_output_mock.assert_called_once()
@patch('thefuck.output_readers.rerun.Popen')
def test_get_output_invalid_continuation_byte(self, popen_mock):
output = b'ls: illegal option -- \xc3\nusage: ls [-@ABC...] [file ...]\n'
expected = u'ls: illegal option -- \ufffd\nusage: ls [-@ABC...] [file ...]\n'
popen_mock.return_value.stdout.read.return_value = output
actual = rerun.get_output('', '')
assert actual == expected
@patch('thefuck.output_readers.rerun._wait_output')
def test_get_output_unicode_misspell(self, wait_output_mock):
rerun.get_output(u'pácman', u'pácman')
wait_output_mock.assert_called_once()
def test_wait_output_is_slow(self, settings):
assert rerun._wait_output(Mock(), True)
self.proc_mock.wait.assert_called_once_with(settings.wait_slow_command)

View File

@@ -39,18 +39,28 @@ def composer_not_command_one_of_this():
)
def test_match(composer_not_command, composer_not_command_one_of_this):
@pytest.fixture
def composer_require_instead_of_install():
return 'Invalid argument package. Use "composer require package" instead to add packages to your composer.json.'
def test_match(composer_not_command, composer_not_command_one_of_this, composer_require_instead_of_install):
assert match(Command('composer udpate',
composer_not_command))
assert match(Command('composer pdate',
composer_not_command_one_of_this))
assert match(Command('composer install package',
composer_require_instead_of_install))
assert not match(Command('ls update', composer_not_command))
def test_get_new_command(composer_not_command, composer_not_command_one_of_this):
def test_get_new_command(composer_not_command, composer_not_command_one_of_this, composer_require_instead_of_install):
assert (get_new_command(Command('composer udpate',
composer_not_command))
== 'composer update')
assert (get_new_command(Command('composer pdate',
composer_not_command_one_of_this))
== 'composer selfupdate')
assert (get_new_command(Command('composer install package',
composer_require_instead_of_install))
== 'composer require package')

View File

@@ -0,0 +1,70 @@
import pytest
from thefuck.rules.git_branch_0flag import get_new_command, match
from thefuck.types import Command
@pytest.fixture
def output_branch_exists():
return "fatal: A branch named 'bar' already exists."
@pytest.mark.parametrize(
"script",
[
"git branch 0a",
"git branch 0d",
"git branch 0f",
"git branch 0r",
"git branch 0v",
"git branch 0d foo",
"git branch 0D foo",
],
)
def test_match(script, output_branch_exists):
assert match(Command(script, output_branch_exists))
@pytest.mark.parametrize(
"script",
[
"git branch -a",
"git branch -r",
"git branch -v",
"git branch -d foo",
"git branch -D foo",
],
)
def test_not_match(script, output_branch_exists):
assert not match(Command(script, ""))
@pytest.mark.parametrize(
"script, new_command",
[
("git branch 0a", "git branch -D 0a && git branch -a"),
("git branch 0v", "git branch -D 0v && git branch -v"),
("git branch 0d foo", "git branch -D 0d && git branch -d foo"),
("git branch 0D foo", "git branch -D 0D && git branch -D foo"),
("git branch 0l 'maint-*'", "git branch -D 0l && git branch -l 'maint-*'"),
("git branch 0u upstream", "git branch -D 0u && git branch -u upstream"),
],
)
def test_get_new_command_branch_exists(script, output_branch_exists, new_command):
assert get_new_command(Command(script, output_branch_exists)) == new_command
@pytest.fixture
def output_not_valid_object():
return "fatal: Not a valid object name: 'bar'."
@pytest.mark.parametrize(
"script, new_command",
[
("git branch 0l 'maint-*'", "git branch -l 'maint-*'"),
("git branch 0u upstream", "git branch -u upstream"),
],
)
def test_get_new_command_not_valid_object(script, output_not_valid_object, new_command):
assert get_new_command(Command(script, output_not_valid_object)) == new_command

View File

@@ -0,0 +1,38 @@
import pytest
from thefuck.rules.git_commit_add import match, get_new_command
from thefuck.types import Command
@pytest.mark.parametrize(
"script, output",
[
('git commit -m "test"', "no changes added to commit"),
("git commit", "no changes added to commit"),
],
)
def test_match(output, script):
assert match(Command(script, output))
@pytest.mark.parametrize(
"script, output",
[
('git commit -m "test"', " 1 file changed, 15 insertions(+), 14 deletions(-)"),
("git branch foo", ""),
("git checkout feature/test_commit", ""),
("git push", ""),
],
)
def test_not_match(output, script):
assert not match(Command(script, output))
@pytest.mark.parametrize(
"script, new_command",
[
("git commit", ["git commit -a", "git commit -p"]),
('git commit -m "foo"', ['git commit -a -m "foo"', 'git commit -p -m "foo"']),
],
)
def test_get_new_command(script, new_command):
assert get_new_command(Command(script, "")) == new_command

View File

@@ -0,0 +1,47 @@
import pytest
from thefuck.rules.git_main_master import match, get_new_command
from thefuck.types import Command
@pytest.fixture
def output(branch_name):
if not branch_name:
return ""
output_str = u"error: pathspec '{}' did not match any file(s) known to git"
return output_str.format(branch_name)
@pytest.mark.parametrize(
"script, branch_name",
[
("git checkout main", "main"),
("git checkout master", "master"),
("git show main", "main"),
],
)
def test_match(script, branch_name, output):
assert match(Command(script, output))
@pytest.mark.parametrize(
"script, branch_name",
[
("git checkout master", ""),
("git checkout main", ""),
("git checkout wibble", "wibble"),
],
)
def test_not_match(script, branch_name, output):
assert not match(Command(script, output))
@pytest.mark.parametrize(
"script, branch_name, new_command",
[
("git checkout main", "main", "git checkout master"),
("git checkout master", "master", "git checkout main"),
("git checkout wibble", "wibble", "git checkout wibble"),
],
)
def test_get_new_command(script, branch_name, new_command, output):
assert get_new_command(Command(script, output)) == new_command

View File

@@ -1,27 +1,20 @@
import pytest
from thefuck.types import Command
from thefuck.rules.git_push_without_commits import (
fix,
get_new_command,
match,
)
command = 'git push -u origin master'
expected_error = '''
error: src refspec master does not match any.
error: failed to push some refs to 'git@github.com:User/repo.git'
'''
from thefuck.rules.git_push_without_commits import get_new_command, match
@pytest.mark.parametrize('command', [Command(command, expected_error)])
def test_match(command):
assert match(command)
def test_match():
script = "git push -u origin master"
output = "error: src refspec master does not match any\nerror: failed to..."
assert match(Command(script, output))
@pytest.mark.parametrize('command, result', [(
Command(command, expected_error),
fix.format(command=command),
)])
def test_get_new_command(command, result):
assert get_new_command(command) == result
def test_not_match():
script = "git push -u origin master"
assert not match(Command(script, "Everything up-to-date"))
def test_get_new_command():
script = "git push -u origin master"
output = "error: src refspec master does not match any\nerror: failed to..."
new_command = 'git commit -m "Initial commit" && git push -u origin master'
assert get_new_command(Command(script, output)) == new_command

View File

@@ -8,11 +8,11 @@ from thefuck.types import Command
def all_executables(mocker):
return mocker.patch(
'thefuck.rules.missing_space_before_subcommand.get_all_executables',
return_value=['git', 'ls', 'npm'])
return_value=['git', 'ls', 'npm', 'w', 'watch'])
@pytest.mark.parametrize('script', [
'gitbranch', 'ls-la', 'npminstall'])
'gitbranch', 'ls-la', 'npminstall', 'watchls'])
def test_match(script):
assert match(Command(script, ''))
@@ -25,6 +25,7 @@ def test_not_match(script):
@pytest.mark.parametrize('script, result', [
('gitbranch', 'git branch'),
('ls-la', 'ls -la'),
('npminstall webpack', 'npm install webpack')])
('npminstall webpack', 'npm install webpack'),
('watchls', 'watch ls')])
def test_get_new_command(script, result):
assert get_new_command(Command(script, '')) == result

View File

@@ -0,0 +1,46 @@
import pytest
from thefuck.rules.rails_migrations_pending import match, get_new_command
from thefuck.types import Command
output_env_development = '''
Migrations are pending. To resolve this issue, run:
rails db:migrate RAILS_ENV=development
'''
output_env_test = '''
Migrations are pending. To resolve this issue, run:
bin/rails db:migrate RAILS_ENV=test
'''
@pytest.mark.parametrize(
"command",
[
Command("", output_env_development),
Command("", output_env_test),
],
)
def test_match(command):
assert match(command)
@pytest.mark.parametrize(
"command",
[
Command("Environment data not found in the schema. To resolve this issue, run: \n\n", ""),
],
)
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize(
"command, new_command",
[
(Command("bin/rspec", output_env_development), "rails db:migrate RAILS_ENV=development && bin/rspec"),
(Command("bin/rspec", output_env_test), "bin/rails db:migrate RAILS_ENV=test && bin/rspec"),
],
)
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@@ -8,7 +8,15 @@ def output():
return "$: command not found"
@pytest.mark.parametrize("script", ["$ cd newdir", " $ cd newdir"])
@pytest.mark.parametrize(
"script",
[
"$ cd newdir",
" $ cd newdir",
"$ $ cd newdir"
" $ $ cd newdir",
],
)
def test_match(script, output):
assert match(Command(script, output))
@@ -31,7 +39,9 @@ def test_not_match(command):
"script, new_command",
[
("$ cd newdir", "cd newdir"),
("$ $ cd newdir", "cd newdir"),
("$ python3 -m virtualenv env", "python3 -m virtualenv env"),
(" $ $ $ python3 -m virtualenv env", "python3 -m virtualenv env"),
],
)
def test_get_new_command(script, new_command, output):

View File

@@ -0,0 +1,30 @@
import pytest
from thefuck.rules.wrong_hyphen_before_subcommand import match, get_new_command
from thefuck.types import Command
@pytest.fixture(autouse=True)
def get_all_executables(mocker):
mocker.patch(
"thefuck.rules.wrong_hyphen_before_subcommand.get_all_executables",
return_value=["git", "apt", "apt-get", "ls", "pwd"],
)
@pytest.mark.parametrize("script", ["git-log", "apt-install python"])
def test_match(script):
assert match(Command(script, ""))
@pytest.mark.parametrize("script", ["ls -la", "git2-make", "apt-get install python"])
def test_not_match(script):
assert not match(Command(script, ""))
@pytest.mark.parametrize(
"script, new_command",
[("git-log", "git log"), ("apt-install python", "apt install python")],
)
def test_get_new_command(script, new_command):
assert get_new_command(Command(script, "")) == new_command

View File

@@ -87,8 +87,11 @@ class TestFish(object):
def test_app_alias_alter_history(self, settings, shell):
settings.alter_history = True
assert 'builtin history delete' in shell.app_alias('FUCK')
assert 'builtin history merge' in shell.app_alias('FUCK')
assert (
'builtin history delete --exact --case-sensitive -- $fucked_up_command\n'
in shell.app_alias('FUCK')
)
assert 'builtin history merge\n' in shell.app_alias('FUCK')
settings.alter_history = False
assert 'builtin history delete' not in shell.app_alias('FUCK')
assert 'builtin history merge' not in shell.app_alias('FUCK')

View File

@@ -6,7 +6,11 @@ from thefuck.types import Command
@pytest.mark.parametrize('called, command, output', [
('git co', 'git checkout', "19:22:36.299340 git.c:282 trace: alias expansion: co => 'checkout'"),
('git com file', 'git commit --verbose file',
"19:23:25.470911 git.c:282 trace: alias expansion: com => 'commit' '--verbose'")])
"19:23:25.470911 git.c:282 trace: alias expansion: com => 'commit' '--verbose'"),
('git com -m "Initial commit"', 'git commit -m "Initial commit"',
"19:22:36.299340 git.c:282 trace: alias expansion: com => 'commit'"),
('git br -d some_branch', 'git branch -d some_branch',
"19:22:36.299340 git.c:282 trace: alias expansion: br => 'branch'")])
def test_git_support(called, command, output):
@git_support
def fn(command):
@@ -23,9 +27,10 @@ def test_git_support(called, command, output):
('ls', False),
('cat git', False),
('cat hub', False)])
def test_git_support_match(command, is_git):
@pytest.mark.parametrize('output', ['', None])
def test_git_support_match(command, is_git, output):
@git_support
def fn(command):
return True
assert fn(Command(command, '')) == is_git
assert fn(Command(command, output)) == is_git

View File

@@ -143,7 +143,9 @@ class TestCommand(object):
([''], None),
(['', ''], None),
(['ls', '-la'], 'ls -la'),
(['ls'], 'ls')])
(['ls'], 'ls'),
(['echo \\ '], 'echo \\ '),
(['echo \\\n'], 'echo \\\n')])
def test_from_script(self, script, result):
if result:
assert Command.from_raw_script(script).script == result

View File

@@ -146,6 +146,8 @@ def test_get_all_matched_commands(stderr, result):
@pytest.mark.usefixtures('no_memoize')
@pytest.mark.parametrize('script, names, result', [
('/usr/bin/git diff', ['git', 'hub'], True),
('/bin/hdfs dfs -rm foo', ['hdfs'], True),
('git diff', ['git', 'hub'], True),
('hub diff', ['git', 'hub'], True),
('hg diff', ['git', 'hub'], False)])
@@ -155,6 +157,8 @@ def test_is_app(script, names, result):
@pytest.mark.usefixtures('no_memoize')
@pytest.mark.parametrize('script, names, result', [
('/usr/bin/git diff', ['git', 'hub'], True),
('/bin/hdfs dfs -rm foo', ['hdfs'], True),
('git diff', ['git', 'hub'], True),
('hub diff', ['git', 'hub'], True),
('hg diff', ['git', 'hub'], False)])
@@ -231,7 +235,7 @@ class TestCache(object):
class TestGetValidHistoryWithoutCurrent(object):
@pytest.yield_fixture(autouse=True)
@pytest.fixture(autouse=True)
def fail_on_warning(self):
warnings.simplefilter('error')
yield

View File

@@ -1,4 +1,5 @@
import six
from ..conf import settings
from ..logs import warn
from ..shells import shell
from ..utils import which
@@ -23,4 +24,5 @@ def _get_alias(known_args):
def print_alias(known_args):
settings.init(known_args)
print(_get_alias(known_args))

View File

@@ -12,7 +12,7 @@ from ..utils import get_alias, get_all_executables
def _get_raw_command(known_args):
if known_args.force_command:
return known_args.force_command
return [known_args.force_command]
elif not os.environ.get('TF_HISTORY'):
return known_args.command
else:

View File

@@ -7,7 +7,7 @@ import os # noqa: E402
import sys # noqa: E402
from .. import logs # noqa: E402
from ..argument_parser import Parser # noqa: E402
from ..utils import get_installation_info # noqa: E402
from ..utils import get_installation_version # noqa: E402
from ..shells import shell # noqa: E402
from .alias import print_alias # noqa: E402
from .fix_command import fix_command # noqa: E402
@@ -20,7 +20,7 @@ def main():
if known_args.help:
parser.print_help()
elif known_args.version:
logs.version(get_installation_info().version,
logs.version(get_installation_version(),
sys.version.split()[0], shell.info())
# It's important to check if an alias is being requested before checking if
# `TF_HISTORY` is in `os.environ`, otherwise it might mess with subshells.

View File

@@ -40,6 +40,9 @@ def _group_by_calls(log):
def _get_script_group_lines(grouped, script):
if six.PY2:
script = script.encode('utf-8')
parts = shlex.split(script)
for script_line, lines in reversed(grouped):

View File

@@ -1,5 +1,6 @@
import os
import shlex
import six
from subprocess import Popen, PIPE, STDOUT
from psutil import AccessDenied, Process, TimeoutExpired
from .. import logs
@@ -53,6 +54,9 @@ def get_output(script, expanded):
env = dict(os.environ)
env.update(settings.env)
if six.PY2:
expanded = expanded.encode('utf-8')
split_expand = shlex.split(expanded)
is_slow = split_expand[0] in settings.slow_commands if split_expand else False
with logs.debug_time(u'Call: {}; with env: {}; is slow: {}'.format(
@@ -60,7 +64,7 @@ def get_output(script, expanded):
result = Popen(expanded, shell=True, stdin=PIPE,
stdout=PIPE, stderr=STDOUT, env=env)
if _wait_output(result, is_slow):
output = result.stdout.read().decode('utf-8')
output = result.stdout.read().decode('utf-8', errors='replace')
logs.debug(u'Received output: {}'.format(output))
return output
else:

View File

@@ -5,12 +5,18 @@ from thefuck.utils import replace_argument, for_app
@for_app('composer')
def match(command):
return (('did you mean this?' in command.output.lower()
or 'did you mean one of these?' in command.output.lower()))
or 'did you mean one of these?' in command.output.lower())) or (
"install" in command.script_parts and "composer require" in command.output.lower()
)
def get_new_command(command):
broken_cmd = re.findall(r"Command \"([^']*)\" is not defined", command.output)[0]
new_cmd = re.findall(r'Did you mean this\?[^\n]*\n\s*([^\n]*)', command.output)
if not new_cmd:
new_cmd = re.findall(r'Did you mean one of these\?[^\n]*\n\s*([^\n]*)', command.output)
return replace_argument(command.script, broken_cmd, new_cmd[0].strip())
if "install" in command.script_parts and "composer require" in command.output.lower():
broken_cmd, new_cmd = "install", "require"
else:
broken_cmd = re.findall(r"Command \"([^']*)\" is not defined", command.output)[0]
new_cmd = re.findall(r'Did you mean this\?[^\n]*\n\s*([^\n]*)', command.output)
if not new_cmd:
new_cmd = re.findall(r'Did you mean one of these\?[^\n]*\n\s*([^\n]*)', command.output)
new_cmd = new_cmd[0].strip()
return replace_argument(command.script, broken_cmd, new_cmd)

View File

@@ -1,4 +1,5 @@
from thefuck.utils import for_app
from thefuck.shells import shell
@for_app('docker')
@@ -9,4 +10,4 @@ def match(command):
def get_new_command(command):
return 'docker login && {}'.format(command.script)
return shell.and_('docker login', command.script)

View File

@@ -0,0 +1,24 @@
from thefuck.shells import shell
from thefuck.specific.git import git_support
from thefuck.utils import memoize
@memoize
def first_0flag(script_parts):
return next((p for p in script_parts if len(p) == 2 and p.startswith("0")), None)
@git_support
def match(command):
return command.script_parts[1] == "branch" and first_0flag(command.script_parts)
@git_support
def get_new_command(command):
branch_name = first_0flag(command.script_parts)
fixed_flag = branch_name.replace("0", "-")
fixed_script = command.script.replace(branch_name, fixed_flag)
if "A branch named '" in command.output and "' already exists." in command.output:
delete_branch = u"git branch -D {}".format(branch_name)
return shell.and_(delete_branch, fixed_script)
return fixed_script

View File

@@ -0,0 +1,17 @@
from thefuck.utils import eager, replace_argument
from thefuck.specific.git import git_support
@git_support
def match(command):
return (
"commit" in command.script_parts
and "no changes added to commit" in command.output
)
@eager
@git_support
def get_new_command(command):
for opt in ("-a", "-p"):
yield replace_argument(command.script, "commit", "commit {}".format(opt))

View File

@@ -23,5 +23,5 @@ def get_new_command(command):
)
priority = 900
priority = 1100
requires_output = False

View File

@@ -0,0 +1,16 @@
from thefuck.specific.git import git_support
@git_support
def match(command):
return "'master'" in command.output or "'main'" in command.output
@git_support
def get_new_command(command):
if "'master'" in command.output:
return command.script.replace("master", "main")
return command.script.replace("main", "master")
priority = 1200

View File

@@ -1,14 +1,12 @@
import re
from thefuck.shells import shell
from thefuck.specific.git import git_support
fix = u'git commit -m "Initial commit." && {command}'
refspec_does_not_match = re.compile(r'src refspec \w+ does not match any\.')
@git_support
def match(command):
return bool(refspec_does_not_match.search(command.output))
return bool(re.search(r"src refspec \w+ does not match any", command.output))
def get_new_command(command):
return fix.format(command=command.script)
return shell.and_('git commit -m "Initial commit"', command.script)

View File

@@ -14,7 +14,7 @@ def get_golang_commands():
if which('go'):
get_docker_commands = cache(which('go'))(get_golang_commands)
get_golang_commands = cache(which('go'))(get_golang_commands)
@for_app('go')

View File

@@ -5,7 +5,7 @@ from thefuck.utils import for_app, eager, replace_command
regex = re.compile(r"Task '(.*)' (is ambiguous|not found)")
@for_app('gradle', './gradlew')
@for_app('gradle', 'gradlew')
def match(command):
return regex.findall(command.output)

View File

@@ -4,7 +4,7 @@ from thefuck.utils import get_all_executables, memoize
@memoize
def _get_executable(script_part):
for executable in get_all_executables():
if script_part.startswith(executable):
if len(executable) > 1 and script_part.startswith(executable):
return executable

View File

@@ -0,0 +1,14 @@
import re
from thefuck.shells import shell
SUGGESTION_REGEX = r"To resolve this issue, run:\s+(.*?)\n"
def match(command):
return "Migrations are pending. To resolve this issue, run:" in command.output
def get_new_command(command):
migration_script = re.search(SUGGESTION_REGEX, command.output).group(1)
return shell.and_(migration_script, command.script)

View File

@@ -1,4 +1,5 @@
"""Fixes error for commands containing the shell prompt symbol '$'.
"""Fixes error for commands containing one or more occurrences of the shell
prompt symbol '$'.
This usually happens when commands are copied from documentations
including them in their code blocks.
@@ -19,4 +20,4 @@ def match(command):
def get_new_command(command):
return command.script.replace("$", "", 1).strip()
return command.script.lstrip("$ ")

View File

@@ -0,0 +1,20 @@
from thefuck.utils import get_all_executables
from thefuck.specific.sudo import sudo_support
@sudo_support
def match(command):
first_part = command.script_parts[0]
if "-" not in first_part or first_part in get_all_executables():
return False
cmd, _ = first_part.split("-", 1)
return cmd in get_all_executables()
@sudo_support
def get_new_command(command):
return command.script.replace("-", " ", 1)
priority = 4500
requires_output = False

View File

@@ -52,7 +52,7 @@ class Fish(Generic):
if settings.alter_history:
alter_history = (' builtin history delete --exact'
' --case-sensitive -- $fucked_up_command\n'
' builtin history merge ^ /dev/null\n')
' builtin history merge\n')
else:
alter_history = ''
# It is VERY important to have the variables declared WITHIN the alias

View File

@@ -14,7 +14,7 @@ def git_support(fn, command):
return False
# perform git aliases expansion
if 'trace: alias expansion:' in command.output:
if command.output and 'trace: alias expansion:' in command.output:
search = re.search("trace: alias expansion: ([^ ]*) => ([^\n]*)",
command.output)
alias = search.group(1)
@@ -25,7 +25,7 @@ def git_support(fn, command):
# eg. 'git commit'
expansion = ' '.join(shell.quote(part)
for part in shell.split_command(search.group(2)))
new_script = command.script.replace(alias, expansion)
new_script = re.sub(r"\b{}\b".format(alias), expansion, command.script)
command = command.update(script=new_script)

View File

@@ -180,13 +180,13 @@ def is_app(command, *app_names, **kwargs):
raise TypeError("got an unexpected keyword argument '{}'".format(kwargs.keys()))
if len(command.script_parts) > at_least:
return command.script_parts[0] in app_names
return os.path.basename(command.script_parts[0]) in app_names
return False
def for_app(*app_names, **kwargs):
"""Specifies that matching script is for on of app names."""
"""Specifies that matching script is for one of app names."""
def _for_app(fn, command):
if is_app(command, *app_names, **kwargs):
return fn(command)
@@ -294,10 +294,15 @@ def cache(*depends_on):
cache.disabled = False
def get_installation_info():
import pkg_resources
def get_installation_version():
try:
from importlib.metadata import version
return pkg_resources.require('thefuck')[0]
return version('thefuck')
except ImportError:
import pkg_resources
return pkg_resources.require('thefuck')[0].version
def get_alias():
@@ -339,4 +344,4 @@ def format_raw_script(raw_script):
else:
script = ' '.join(raw_script)
return script.strip()
return script.lstrip()