mirror of
https://github.com/nvbn/thefuck.git
synced 2025-11-01 15:42:06 +00:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
841e3f9e13 | ||
|
|
0f4a523dc4 | ||
|
|
c719712b62 | ||
|
|
51e4e87280 | ||
|
|
7b7c150bb7 | ||
|
|
2a166a7dec | ||
|
|
8fa10b1049 | ||
|
|
7f3442747e | ||
|
|
8bebce331e | ||
|
|
dbc435c040 | ||
|
|
30c90bccaa | ||
|
|
8e8c80c227 | ||
|
|
c2df71caed | ||
|
|
eb05b28c5b | ||
|
|
a2a6cbdc70 | ||
|
|
58ddd4338a | ||
|
|
58f61d8090 | ||
|
|
0668822abb | ||
|
|
711feb4df5 | ||
|
|
70a42b54ab | ||
|
|
11b70526f7 | ||
|
|
55922e4dbe | ||
|
|
799f4127ca | ||
|
|
fe1942866b | ||
|
|
13fda64d6e | ||
|
|
6111523034 | ||
|
|
24576b30b2 | ||
|
|
373f445e9b | ||
|
|
54253027e3 | ||
|
|
9201ce79cf | ||
|
|
7f97818663 | ||
|
|
2f4adcf3cb | ||
|
|
06e14afd17 | ||
|
|
3651b0fa0c | ||
|
|
d723bb71b7 | ||
|
|
b2e1886de8 |
10
.devcontainer/Dockerfile
Normal file
10
.devcontainer/Dockerfile
Normal 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
|
||||
39
.devcontainer/devcontainer.json
Normal file
39
.devcontainer/devcontainer.json
Normal 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"
|
||||
}
|
||||
@@ -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.
|
||||
42
README.md
42
README.md
@@ -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` – changes `git branch -d` to `git checkout master && git branch -D` when trying to delete a checked out branch;
|
||||
* `git_branch_exists` – offers `git branch -d foo`, `git branch -D foo` or `git checkout foo` when creating a branch that already exists;
|
||||
* `git_branch_list` – catches `git branch list` in place of `git branch` and removes created branch;
|
||||
* `git_branch_0flag` – fixes commands such as `git branch 0v` and `git branch 0r` removing the created branch;
|
||||
* `git_checkout` – fixes branch name or creates new branch;
|
||||
* `git_clone_git_clone` – replaces `git clone git clone ...` with `git clone ...`
|
||||
* `git_commit_add` – offers `git commit -a ...` or `git commit -p ...` after previous commit if it failed because nothing was staged;
|
||||
* `git_commit_amend` – offers `git commit --amend` after previous commit;
|
||||
* `git_commit_reset` – offers `git reset HEAD~` after previous commit;
|
||||
* `git_diff_no_index` – adds `--no-index` to previous `git diff` on untracked files;
|
||||
@@ -236,6 +244,7 @@ following rules are enabled by default:
|
||||
* `git_help_aliased` – fixes `git help <alias>` commands replacing <alias> with the aliased command;
|
||||
* `git_hook_bypass` – adds `--no-verify` flag previous to `git am`, `git commit`, or `git push` command;
|
||||
* `git_lfs_mistype` – fixes mistyped `git lfs <command>` commands;
|
||||
* `git_main_master` – fixes incorrect branch name between `main` and `master`
|
||||
* `git_merge` – adds remote to branch names;
|
||||
* `git_merge_unrelated` – adds `--allow-unrelated-histories` when required
|
||||
* `git_not_command` – fixes wrong git commands like `git brnch`;
|
||||
@@ -243,7 +252,7 @@ following rules are enabled by default:
|
||||
* `git_pull_clone` – clones instead of pulling when the repo does not exist;
|
||||
* `git_pull_uncommitted_changes` – stashes changes before pulling and pops them afterwards;
|
||||
* `git_push` – adds `--set-upstream origin $branch` to previous failed `git push`;
|
||||
* `git_push_different_branch_names` – fixes pushes when local brach name does not match remote branch name;
|
||||
* `git_push_different_branch_names` – fixes pushes when local branch name does not match remote branch name;
|
||||
* `git_push_pull` – runs `git pull` when `push` was rejected;
|
||||
* `git_push_without_commits` – Creates an initial commit if you forget and only `git add .`, when setting up a new project;
|
||||
* `git_rebase_no_changes` – 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` – prepends `./` when script/binary exists;
|
||||
* `heroku_multiple_apps` – add `--app <app>` to `heroku` commands like `heroku pg`;
|
||||
* `heroku_not_command` – fixes wrong `heroku` commands like `heroku log`;
|
||||
* `history` – tries to replace command with most similar command from history;
|
||||
* `history` – tries to replace command with the most similar command from history;
|
||||
* `hostscli` – tries to fix `hostscli` usage;
|
||||
* `ifconfig_device_not_found` – fixes wrong device names like `wlan0` to `wlp2s0`;
|
||||
* `java` – removes `.java` extension when running Java programs;
|
||||
@@ -283,7 +292,7 @@ following rules are enabled by default:
|
||||
* `man_no_space` – fixes man commands without spaces, for example `mandiff`;
|
||||
* `mercurial` – fixes wrong `hg` commands;
|
||||
* `missing_space_before_subcommand` – fixes command with missing space like `npminstall`;
|
||||
* `mkdir_p` – adds `-p` when you try to create a directory without parent;
|
||||
* `mkdir_p` – adds `-p` when you try to create a directory without a parent;
|
||||
* `mvn_no_command` – adds `clean package` to `mvn`;
|
||||
* `mvn_unknown_lifecycle_phase` – fixes misspelled life cycle phases with `mvn`;
|
||||
* `npm_missing_script` – fixes `npm` custom script name in `npm run-script <script>`;
|
||||
@@ -302,16 +311,17 @@ following rules are enabled by default:
|
||||
* `python_execute` – appends missing `.py` when executing Python files;
|
||||
* `python_module_error` – fixes ModuleNotFoundError by trying to `pip install` that module;
|
||||
* `quotation_marks` – fixes uneven usage of `'` and `"` when containing args';
|
||||
* `path_from_history` – replaces not found path with similar absolute path from history;
|
||||
* `path_from_history` – replaces not found path with a similar absolute path from history;
|
||||
* `rails_migrations_pending` – runs pending migrations;
|
||||
* `react_native_command_unrecognized` – fixes unrecognized `react-native` commands;
|
||||
* `remove_shell_prompt_literal` – remove leading shell prompt symbol `$`, common when copying commands from documentations;
|
||||
* `remove_trailing_cedilla` – remove trailing cedillas `ç`, a common typo for european keyboard layouts;
|
||||
* `remove_trailing_cedilla` – remove trailing cedillas `ç`, a common typo for European keyboard layouts;
|
||||
* `rm_dir` – adds `-rf` when you try to remove a directory;
|
||||
* `scm_correction` – corrects wrong scm like `hg log` to `git log`;
|
||||
* `sed_unterminated_s` – adds missing '/' to `sed`'s `s` commands;
|
||||
* `sl_ls` – changes `sl` to `ls`;
|
||||
* `ssh_known_hosts` – removes host from `known_hosts` on warning;
|
||||
* `sudo` – prepends `sudo` to previous command if it failed because of permissions;
|
||||
* `sudo` – prepends `sudo` to the previous command if it failed because of permissions;
|
||||
* `sudo_command_from_user_path` – runs commands from users `$PATH` with `sudo`;
|
||||
* `switch_lang` – switches command from your local layout to en;
|
||||
* `systemctl` – correctly orders parameters of confusing `systemctl`;
|
||||
@@ -322,10 +332,11 @@ following rules are enabled by default:
|
||||
* `tsuru_not_command` – fixes wrong `tsuru` commands like `tsuru shell`;
|
||||
* `tmux` – fixes `tmux` commands;
|
||||
* `unknown_command` – fixes hadoop hdfs-style "unknown command", for example adds missing '-' to the command on `hdfs dfs ls`;
|
||||
* `unsudo` – removes `sudo` from previous command if a process refuses to run on super user privilege.
|
||||
* `unsudo` – removes `sudo` from previous command if a process refuses to run on superuser privilege.
|
||||
* `vagrant_up` – starts up the vagrant instance;
|
||||
* `whois` – fixes `whois` command;
|
||||
* `workon_doesnt_exists` – fixes `virtualenvwrapper` env name os suggests to create new.
|
||||
* `wrong_hyphen_before_subcommand` – removes an improperly placed hyphen (`apt-install` -> `apt install`, `git-log` -> `git log`, etc.)
|
||||
* `yarn_alias` – fixes aliased `yarn` commands like `yarn ls`;
|
||||
* `yarn_command_not_found` – fixes misspelled `yarn` commands;
|
||||
* `yarn_command_replaced` – fixes replaced `yarn` commands;
|
||||
@@ -425,15 +436,15 @@ Several *The Fuck* parameters can be changed in the file `$XDG_CONFIG_HOME/thefu
|
||||
* `rules` – list of enabled rules, by default `thefuck.const.DEFAULT_RULES`;
|
||||
* `exclude_rules` – list of disabled rules, by default `[]`;
|
||||
* `require_confirmation` – requires confirmation before running new command, by default `True`;
|
||||
* `wait_command` – max amount of time in seconds for getting previous command output;
|
||||
* `wait_command` – the max amount of time in seconds for getting previous command output;
|
||||
* `no_colors` – disable colored output;
|
||||
* `priority` – dict with rules priorities, rule with lower `priority` will be matched first;
|
||||
* `debug` – enables debug output, by default `False`;
|
||||
* `history_limit` – numeric value of how many history commands will be scanned, like `2000`;
|
||||
* `history_limit` – the numeric value of how many history commands will be scanned, like `2000`;
|
||||
* `alter_history` – push fixed command to history, by default `True`;
|
||||
* `wait_slow_command` – max amount of time in seconds for getting previous command output if it in `slow_commands` list;
|
||||
* `slow_commands` – list of slow commands;
|
||||
* `num_close_matches` – maximum number of close matches to suggest, by default `3`.
|
||||
* `num_close_matches` – the maximum number of close matches to suggest, by default `3`.
|
||||
* `excluded_search_path_prefixes` – 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` – list of enabled rules, like `DEFAULT_RULES:rm_root` or `sudo:no_command`;
|
||||
* `THEFUCK_EXCLUDE_RULES` – list of disabled rules, like `git_pull:git_push`;
|
||||
* `THEFUCK_REQUIRE_CONFIRMATION` – require confirmation before running new command, `true/false`;
|
||||
* `THEFUCK_WAIT_COMMAND` – max amount of time in seconds for getting previous command output;
|
||||
* `THEFUCK_WAIT_COMMAND` – the max amount of time in seconds for getting previous command output;
|
||||
* `THEFUCK_NO_COLORS` – disable colored output, `true/false`;
|
||||
* `THEFUCK_PRIORITY` – priority of the rules, like `no_command=9999:apt_get=100`,
|
||||
rule with lower `priority` will be matched first;
|
||||
* `THEFUCK_DEBUG` – enables debug output, `true/false`;
|
||||
* `THEFUCK_HISTORY_LIMIT` – how many history commands will be scanned, like `2000`;
|
||||
* `THEFUCK_ALTER_HISTORY` – push fixed command to history `true/false`;
|
||||
* `THEFUCK_WAIT_SLOW_COMMAND` – max amount of time in seconds for getting previous command output if it in `slow_commands` list;
|
||||
* `THEFUCK_WAIT_SLOW_COMMAND` – the max amount of time in seconds for getting previous command output if it in `slow_commands` list;
|
||||
* `THEFUCK_SLOW_COMMANDS` – list of slow commands, like `lein:gradle`;
|
||||
* `THEFUCK_NUM_CLOSE_MATCHES` – maximum number of close matches to suggest, like `5`.
|
||||
* `THEFUCK_NUM_CLOSE_MATCHES` – the maximum number of close matches to suggest, like `5`.
|
||||
* `THEFUCK_EXCLUDED_SEARCH_PATH_PREFIXES` – 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)
|
||||
|
||||
BIN
example.gif
BIN
example.gif
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 |
2
setup.py
2
setup.py
@@ -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'],
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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')
|
||||
|
||||
70
tests/rules/test_git_branch_0flag.py
Normal file
70
tests/rules/test_git_branch_0flag.py
Normal 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
|
||||
38
tests/rules/test_git_commit_add.py
Normal file
38
tests/rules/test_git_commit_add.py
Normal 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
|
||||
47
tests/rules/test_git_main_master.py
Normal file
47
tests/rules/test_git_main_master.py
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
46
tests/rules/test_rails_migrations_pending.py
Normal file
46
tests/rules/test_rails_migrations_pending.py
Normal 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
|
||||
@@ -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):
|
||||
|
||||
30
tests/rules/test_wrong_hyphen_before_subcommand.py
Normal file
30
tests/rules/test_wrong_hyphen_before_subcommand.py
Normal 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
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
24
thefuck/rules/git_branch_0flag.py
Normal file
24
thefuck/rules/git_branch_0flag.py
Normal 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
|
||||
17
thefuck/rules/git_commit_add.py
Normal file
17
thefuck/rules/git_commit_add.py
Normal 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))
|
||||
@@ -23,5 +23,5 @@ def get_new_command(command):
|
||||
)
|
||||
|
||||
|
||||
priority = 900
|
||||
priority = 1100
|
||||
requires_output = False
|
||||
|
||||
16
thefuck/rules/git_main_master.py
Normal file
16
thefuck/rules/git_main_master.py
Normal 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
14
thefuck/rules/rails_migrations_pending.py
Normal file
14
thefuck/rules/rails_migrations_pending.py
Normal 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)
|
||||
@@ -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("$ ")
|
||||
|
||||
20
thefuck/rules/wrong_hyphen_before_subcommand.py
Normal file
20
thefuck/rules/wrong_hyphen_before_subcommand.py
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user