1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-11-04 09:02:08 +00:00

Compare commits

..

23 Commits
2.1 ... 2.3

Author SHA1 Message Date
nvbn
6c534c52bc Bump to 2.3 2015-07-22 04:45:04 +03:00
nvbn
b4392ba706 #N/A Add heroku_not_command rule 2015-07-22 04:44:37 +03:00
Vladimir Iakovlev
46f918718f Merge pull request #307 from evverx/lc_all
Force LC_ALL to C
2015-07-21 17:11:16 +03:00
Vladimir Iakovlev
d71ce76ae4 Merge pull request #306 from mcarton/hub
Support GitHub's hub command
2015-07-21 16:43:24 +03:00
nvbn
355505a0a8 #N/A Make git_checkout test less dependent on get_closest 2015-07-21 16:40:45 +03:00
Evgeny Vereshchagin
3d425ce831 Force LC_ALL to C
See: http://unix.stackexchange.com/a/87763/120177
2015-07-21 13:39:34 +00:00
mcarton
98a9fb3d7d Remove now redundant checks in git_* rules 2015-07-21 15:35:39 +02:00
nvbn
c8d748e095 Bump to 2.2 2015-07-21 16:31:17 +03:00
nvbn
e0af35819d Merge branch 'master' of github.com:nvbn/thefuck 2015-07-21 16:19:11 +03:00
nvbn
9e4c250e4e #301 Fix bash support on non-eng systems 2015-07-21 16:19:01 +03:00
Vladimir Iakovlev
8c395377f8 Merge pull request #299 from evverx/dnf-history
Add `dnf history` error for the sudo rule
2015-07-21 16:15:08 +03:00
Vladimir Iakovlev
f165523247 Merge pull request #304 from mcarton/fix-git_diff_staged
Fix the `git_diff_staged` rule
2015-07-21 16:12:19 +03:00
mcarton
903abff77e Support hub as well as git in @git_support 2015-07-21 15:06:04 +02:00
Evgeny Vereshchagin
6d39b78824 Add dnf history error for the sudo rule
$ dnf history
You don't have access to the history DB.
2015-07-21 12:56:25 +00:00
mcarton
1285303363 Fix the git_diff_staged rule
The problem was:
```
% git add foo
% git diff foo
% fuck
git diff foo --staged [enter/ctrl+c]
fatal: bad flag '--staged' used after filename
```
2015-07-21 14:06:37 +02:00
nvbn
66e2ec7e3f Merge branch 'mcarton-fix-readme' 2015-07-20 22:49:31 +03:00
nvbn
92cca7b641 #296 Fix [enter/ctrl+c] case in the readme 2015-07-20 22:49:21 +03:00
mcarton
e572cab1f3 Have the README look better 2015-07-20 21:12:39 +02:00
mcarton
33b1536c28 Move misplaced rule in README 2015-07-20 21:07:56 +02:00
mcarton
d4fada8e4c Reflect the new default for require_confirmation 2015-07-20 21:06:59 +02:00
mcarton
afc089bc3c Be more consistent in README 2015-07-20 20:49:21 +02:00
nvbn
300c8f528a #N/A Mention tcsh in readme 2015-07-20 21:27:19 +03:00
nvbn
7b011a504d #N/A Fix tests in travis 2015-07-20 21:24:00 +03:00
26 changed files with 131 additions and 56 deletions

View File

@@ -7,6 +7,7 @@ install:
- pip install -r requirements.txt
- python setup.py develop
- pip install coveralls
- rm -rf build
script:
- export COVERAGE_PYTHON_VERSION=python-${TRAVIS_PYTHON_VERSION:0:1}
- coverage run --source=thefuck,tests -m py.test -v

View File

@@ -14,7 +14,7 @@ E: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)
E: Unable to lock the administration directory (/var/lib/dpkg/), are you root?
➜ fuck
sudo apt-get install vim
sudo apt-get install vim [enter/ctrl+c]
[sudo] password for nvbn:
Reading package lists... Done
...
@@ -29,7 +29,7 @@ To push the current branch and set the remote as upstream, use
➜ fuck
git push --set-upstream origin master
git push --set-upstream origin master [enter/ctrl+c]
Counting objects: 9, done.
...
```
@@ -42,7 +42,7 @@ No command 'puthon' found, did you mean:
zsh: command not found: puthon
➜ fuck
python
python [enter/ctrl+c]
Python 3.4.2 (default, Oct 8 2014, 13:08:17)
...
```
@@ -55,7 +55,7 @@ Did you mean this?
branch
➜ fuck
git branch
git branch [enter/ctrl+c]
* master
```
@@ -67,13 +67,13 @@ Did you mean this?
repl
➜ fuck
lein repl
lein repl [enter/ctrl+c]
nREPL server started on port 54848 on host 127.0.0.1 - nrepl://127.0.0.1:54848
REPL-y 0.3.1
...
```
If you are scared to blindly run the changed command, there is a `require_confirmation`
If you are not scared to blindly run the changed command, there is a `require_confirmation`
[settings](#settings) option:
```bash
@@ -82,7 +82,7 @@ E: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)
E: Unable to lock the administration directory (/var/lib/dpkg/), are you root?
➜ fuck
sudo apt-get install vim [Enter/Ctrl+C]
sudo apt-get install vim
[sudo] password for nvbn:
Reading package lists... Done
...
@@ -112,7 +112,7 @@ eval "$(thefuck-alias)"
eval "$(thefuck-alias FUCK)"
```
[Or in your shell config (Bash, Zsh, Fish, Powershell).](https://github.com/nvbn/thefuck/wiki/Shell-aliases)
[Or in your shell config (Bash, Zsh, Fish, Powershell, tcsh).](https://github.com/nvbn/thefuck/wiki/Shell-aliases)
Changes will be available only in a new shell session.
To make them available immediately, run `source ~/.bashrc` (or your shell config file like `.zshrc`).
@@ -138,17 +138,17 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `cd_parent` – changes `cd..` to `cd ..`;
* `composer_not_command` – fixes composer command name;
* `cp_omitting_directory` – adds `-a` when you `cp` directory;
* `cpp11` – add missing `-std=c++11` to `g++` or `clang++`;
* `cpp11` – adds missing `-std=c++11` to `g++` or `clang++`;
* `django_south_ghost` – adds `--delete-ghost-migrations` to failed because ghosts django south migration;
* `django_south_merge` – adds `--merge` to inconsistent django south migration;
* `dry` – fix repetitions like "git git push";
* `dry` – fixes repetitions like "git git push";
* `fix_alt_space` – replaces Alt+Space with Space character;
* `git_add` – fix *"Did you forget to 'git add'?"*;
* `git_add` – fixes *"Did you forget to 'git add'?"*;
* `git_branch_delete` – changes `git branch -d` to `git branch -D`;
* `git_branch_list` – catches `git branch list` in place of `git branch` and removes created branch;
* `git_checkout` – fixes branch name or creates new branch;
* `git_diff_staged` – adds `--staged` to previous `git diff` with unexpected output;
* `git_no_command` – fixes wrong git commands like `git brnch`;
* `git_not_command` – fixes wrong git commands like `git brnch`;
* `git_pull` – sets upstream before executing previous `git pull`;
* `git_pull_clone` – clones instead of pulling when the repo does not exist;
* `git_push` – adds `--set-upstream origin $branch` to previous failed `git push`;
@@ -157,12 +157,13 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `go_run` – appends `.go` extension when compiling/running Go programs
* `grep_recursive` – adds `-r` when you trying to grep directory;
* `has_exists_script` – prepends `./` when script/binary exists;
* `heroku_no_command` – fixes wrong heroku commands like `heroku log`;
* `history` – tries to replace command with most similar command from history;
* `java` – removes `.java` extension when running Java programs;
* `javac` – appends missing `.java` when compiling Java files;
* `lein_not_task` – fixes wrong `lein` tasks like `lein rpl`;
* `ls_lah` – adds -lah to ls;
* `man` – change manual section;
* `man` – changes manual section;
* `man_no_space` – fixes man commands without spaces, for example `mandiff`;
* `mercurial` – fixes wrong `hg` commands;
* `mkdir_p` – adds `-p` when you trying to create directory without parent;
@@ -190,26 +191,29 @@ Enabled by default only on specific platforms:
* `brew_install` – fixes formula name for `brew install`;
* `brew_unknown_command` – fixes wrong brew commands, for example `brew docto/brew doctor`;
* `brew_upgrade` – appends `--all` to `brew upgrade` as per Homebrew's new behaviour;
* `git_push_force` – adds `--force` to a `git push` (may conflict with `git_push_pull`);
* `pacman` – installs app with `pacman` or `yaourt` if it is not installed.
Bundled, but not enabled by default:
* `git_push_force` – adds `--force` to a `git push` (may conflict with `git_push_pull`);
* `rm_root` – adds `--no-preserve-root` to `rm -rf /` command.
## Creating your own rules
For adding your own rule you should create `your-rule-name.py`
in `~/.thefuck/rules`. Rule should contain two functions:
`match(command: Command, settings: Settings) -> bool`
and `get_new_command(command: Command, settings: Settings) -> str`.
Also the rule can contain optional function
`side_effect(command: Command, settings: Settings) -> None` and
in `~/.thefuck/rules`. The rule should contain two functions:
```python
match(command: Command, settings: Settings) -> bool
get_new_command(command: Command, settings: Settings) -> str
```
Also the rule can contain an optional function
`side_effect(command: Command, settings: Settings) -> None` and an
optional boolean `enabled_by_default`.
`Command` has three attributes: `script`, `stdout` and `stderr`.
`Settings` is a special object filled with `~/.thefuck/settings.py` and values from env, [more](#settings).
`Settings` is a special object filled with `~/.thefuck/settings.py` and values from env ([see more below](#settings)).
Simple example of the rule for running script with `sudo`:
@@ -243,7 +247,7 @@ The Fuck has a few settings parameters which can be changed in `~/.thefuck/setti
* `wait_command` – 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`;
* `debug` – enables debug output, by default `False`.
Example of `settings.py`:

View File

@@ -11,7 +11,7 @@ elif (3, 0) < sys.version_info < (3, 3):
' ({}.{} detected).'.format(*sys.version_info[:2]))
sys.exit(-1)
VERSION = '2.1'
VERSION = '2.3'
install_requires = ['psutil', 'colorama', 'six']
extras_require = {':python_version<"3.4"': ['pathlib']}

View File

@@ -41,10 +41,10 @@ def test_not_match(command):
Command('git commit unknown', stderr=did_not_match('unknown')),
'git branch unknown && git commit unknown'),
(['master'],
Command(script='git checkout amster', stderr=did_not_match('amster')),
Command(script='git checkout mster', stderr=did_not_match('mster')),
'git checkout master'),
(['master'],
Command(script='git commit amster', stderr=did_not_match('amster')),
Command(script='git commit mster', stderr=did_not_match('mster')),
'git commit master')])
def test_get_new_command(branches, command, new_command, get_branches):
get_branches.return_value = branches

View File

@@ -3,7 +3,9 @@ from thefuck.rules.git_diff_staged import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [Command(script='git diff')])
@pytest.mark.parametrize('command', [
Command(script='git diff foo'),
Command(script='git diff')])
def test_match(command):
assert match(command, None)
@@ -18,6 +20,7 @@ def test_not_match(command):
@pytest.mark.parametrize('command, new_command', [
(Command('git diff'), 'git diff --staged')])
(Command('git diff'), 'git diff --staged'),
(Command('git diff foo'), 'git diff --staged foo')])
def test_get_new_command(command, new_command):
assert get_new_command(command, None) == new_command

View File

@@ -20,5 +20,5 @@ def test_match(stderr):
def test_get_new_command(stderr):
assert get_new_command(Command(stderr=stderr), None)\
assert get_new_command(Command('git push', stderr=stderr), None)\
== "git push --set-upstream origin master"

View File

@@ -0,0 +1,34 @@
import pytest
from tests.utils import Command
from thefuck.rules.heroku_not_command import match, get_new_command
def suggest_stderr(cmd):
return ''' ! `{}` is not a heroku command.
! Perhaps you meant `logs`, `pg`.
! See `heroku help` for a list of available commands.'''.format(cmd)
no_suggest_stderr = ''' ! `aaaaa` is not a heroku command.
! See `heroku help` for a list of available commands.'''
@pytest.mark.parametrize('cmd', ['log', 'pge'])
def test_match(cmd):
assert match(
Command('heroku {}'.format(cmd), stderr=suggest_stderr(cmd)), None)
@pytest.mark.parametrize('script, stderr', [
('cat log', suggest_stderr('log')),
('heroku aaa', no_suggest_stderr)])
def test_not_match(script, stderr):
assert not match(Command(script, stderr=stderr), None)
@pytest.mark.parametrize('cmd, result', [
('log', 'heroku logs'),
('pge', 'heroku pg')])
def test_get_new_command(cmd, result):
command = Command('heroku {}'.format(cmd), stderr=suggest_stderr(cmd))
assert get_new_command(command, None) == result

View File

@@ -11,6 +11,7 @@ from tests.utils import Command
('need to be root', ''),
('need root', ''),
('must be root', ''),
('You don\'t have access to the history DB.', ''),
('', "error: [Errno 13] Permission denied: '/usr/local/lib/python2.7/dist-packages/ipaddr.py'")])
def test_match(stderr, stdout):
assert match(Command(stderr=stderr, stdout=stdout), None)

View File

@@ -36,6 +36,20 @@ def test_git_support(called, command, stderr):
assert fn(Command(script=called, stderr=stderr), None) == command
@pytest.mark.parametrize('command, is_git', [
('git pull', True),
('hub pull', True),
('git push --set-upstream origin foo', True),
('hub push --set-upstream origin foo', True),
('ls', False),
('cat git', False),
('cat hub', False)])
def test_git_support_match(command, is_git):
@git_support
def fn(command, settings): return True
assert fn(Command(script=command), None) == is_git
def test_memoize():
fn = Mock(__name__='fn')
memoized = memoize(fn)

View File

@@ -31,7 +31,7 @@ DEFAULT_SETTINGS = {'rules': DEFAULT_RULES,
'no_colors': False,
'debug': False,
'priority': {},
'env': {'LANG': 'C', 'GIT_TRACE': '1'}}
'env': {'LC_ALL': 'C', 'LANG': 'C', 'GIT_TRACE': '1'}}
ENV_TO_ATTR = {'THEFUCK_RULES': 'rules',
'THEFUCK_WAIT_COMMAND': 'wait_command',

View File

@@ -4,8 +4,7 @@ from thefuck import utils, shells
@utils.git_support
def match(command, settings):
return ('git' in command.script
and 'did not match any file(s) known to git.' in command.stderr
return ('did not match any file(s) known to git.' in command.stderr
and "Did you forget to 'git add'?" in command.stderr)

View File

@@ -3,7 +3,7 @@ from thefuck import utils
@utils.git_support
def match(command, settings):
return ('git branch -d' in command.script
return ('branch -d' in command.script
and 'If you are sure you want to delete it' in command.stderr)

View File

@@ -4,7 +4,7 @@ from thefuck import utils, shells
@utils.git_support
def match(command, settings):
# catches "git branch list" in place of "git branch"
return command.script.split() == 'git branch list'.split()
return command.script.split()[1:] == 'branch list'.split()
@utils.git_support

View File

@@ -5,8 +5,7 @@ from thefuck import shells, utils
@utils.git_support
def match(command, settings):
return ('git' in command.script
and 'did not match any file(s) known to git.' in command.stderr
return ('did not match any file(s) known to git.' in command.stderr
and "Did you forget to 'git add'?" not in command.stderr)

View File

@@ -3,11 +3,10 @@ from thefuck import utils
@utils.git_support
def match(command, settings):
return ('git' in command.script and
'diff' in command.script and
return ('diff' in command.script and
'--staged' not in command.script)
@utils.git_support
def get_new_command(command, settings):
return '{} --staged'.format(command.script)
return command.script.replace(' diff', ' diff --staged')

View File

@@ -4,8 +4,7 @@ from thefuck.utils import get_closest, git_support
@git_support
def match(command, settings):
return ('git' in command.script
and " is not a git command. See 'git --help'." in command.stderr
return (" is not a git command. See 'git --help'." in command.stderr
and 'Did you mean' in command.stderr)

View File

@@ -3,8 +3,7 @@ from thefuck import shells, utils
@utils.git_support
def match(command, settings):
return ('git' in command.script
and 'pull' in command.script
return ('pull' in command.script
and 'set-upstream' in command.stderr)

View File

@@ -1,11 +1,9 @@
import re
from thefuck import utils, shells
from thefuck import utils
@utils.git_support
def match(command, settings):
return ('git pull' in command.script
and 'fatal: Not a git repository' in command.stderr
return ('fatal: Not a git repository' in command.stderr
and "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)." in command.stderr)

View File

@@ -3,8 +3,7 @@ from thefuck import utils
@utils.git_support
def match(command, settings):
return ('git' in command.script
and 'push' in command.script
return ('push' in command.script
and 'set-upstream' in command.stderr)

View File

@@ -3,8 +3,7 @@ from thefuck import utils
@utils.git_support
def match(command, settings):
return ('git' in command.script
and 'push' in command.script
return ('push' in command.script
and '! [rejected]' in command.stderr
and 'failed to push some refs to' in command.stderr
and 'Updates were rejected because the tip of your current branch is behind' in command.stderr)

View File

@@ -4,8 +4,7 @@ from thefuck.shells import and_
@utils.git_support
def match(command, settings):
return ('git' in command.script
and 'push' in command.script
return ('push' in command.script
and '! [rejected]' in command.stderr
and 'failed to push some refs to' in command.stderr
and 'Updates were rejected because the tip of your current branch is behind' in command.stderr)

View File

@@ -5,7 +5,7 @@ from thefuck import shells, utils
def match(command, settings):
# catches "Please commit or stash them" and "Please, commit your changes or
# stash them before you can switch branches."
return 'git' in command.script and 'or stash them' in command.stderr
return 'or stash them' in command.stderr
@utils.git_support

View File

@@ -0,0 +1,20 @@
import re
from thefuck.utils import get_closest
def match(command, settings):
return command.script.startswith('heroku') and \
'is not a heroku command' in command.stderr and \
'Perhaps you meant' in command.stderr
def _get_suggests(stderr):
for line in stderr.split('\n'):
if 'Perhaps you meant' in line:
return re.findall(r'`([^`]+)`', line)
def get_new_command(command, settings):
wrong = re.findall(r'`(\w+)` is not a heroku command', command.stderr)[0]
correct = get_closest(wrong, _get_suggests(command.stderr))
return command.script.replace(' {}'.format(wrong), ' {}'.format(correct), 1)

View File

@@ -14,6 +14,7 @@ patterns = ['permission denied',
'need to be root',
'need root',
'only root can do that',
'You don\'t have access to the history DB.',
'authentication is required']

View File

@@ -100,7 +100,6 @@ class Bash(Generic):
return u'{}\n'.format(command_script)
def _script_from_history(self, line):
print(line)
return line

View File

@@ -75,12 +75,19 @@ def sudo_support(fn):
def git_support(fn):
"""Resolve git aliases."""
"""Resolves git aliases and supports testing for both git and hub."""
@wraps(fn)
def wrapper(command, settings):
if (command.script.startswith('git') and
'trace: alias expansion:' in command.stderr):
# supports GitHub's `hub` command
# which is recommended to be used with `alias git=hub`
# but at this point, shell aliases have already been resolved
is_git_cmd = command.script.startswith(('git', 'hub'))
if not is_git_cmd:
return False
# perform git aliases expansion
if 'trace: alias expansion:' in command.stderr:
search = re.search("trace: alias expansion: ([^ ]*) => ([^\n]*)",
command.stderr)
alias = search.group(1)
@@ -93,6 +100,7 @@ def git_support(fn):
new_script = command.script.replace(alias, expansion)
command = Command._replace(command, script=new_script)
return fn(command, settings)
return wrapper