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

Compare commits

...

35 Commits
3.5 ... 3.8

Author SHA1 Message Date
nvbn
c6af8409d9 Bump to 3.8 2016-04-08 14:36:53 +03:00
Vladimir Iakovlev
95e7d00aec Merge pull request #492 from scorphus/overridden-aliases
Treat overridden aliases in a better way
2016-04-07 13:09:08 +03:00
Pablo Santiago Blum de Aguiar
cdccf1881e #253: Use a better name for that env var 2016-04-06 23:11:19 -03:00
Pablo Santiago Blum de Aguiar
db6053b301 #253: Update default overridden aliases with user's 2016-04-06 22:58:08 -03:00
nvbn
183b70c8b8 Merge branch 'scorphus-git-branch-exists' 2016-04-03 14:08:26 +03:00
nvbn
5e0cc8c703 #491: yield possible fixes in git_branch_exists rule 2016-04-03 14:07:39 +03:00
nvbn
1aa2ec1795 Merge branch 'git-branch-exists' of https://github.com/scorphus/thefuck into scorphus-git-branch-exists 2016-04-03 14:02:32 +03:00
Pablo Santiago Blum de Aguiar
0c98053f74 #N/A Add a new rule git_branch_exists 2016-04-03 00:09:15 -03:00
Vladimir Iakovlev
17b2fba48d Merge pull request #489 from scorphus/improve-readme
Improve readme
2016-04-02 01:44:51 +03:00
Pablo Santiago Blum de Aguiar
43886c38ff #N/A Add more fancy badges 2016-03-31 22:07:10 -03:00
Pablo Santiago Blum de Aguiar
9070748a86 #N/A Use reference links 2016-03-31 22:06:01 -03:00
Pablo Santiago Blum de Aguiar
61de6f4a51 #N/A Reformat parts of README.md 2016-03-31 15:53:32 -03:00
Vladimir Iakovlev
d102af41d9 #488 Add AppVeyor badge 2016-03-31 04:50:40 +03:00
Vladimir Iakovlev
b7002bb9f9 Merge pull request #488 from scorphus/app-veyor
App veyor
2016-03-31 04:44:44 +03:00
Pablo Santiago Blum de Aguiar
18b4f5df6a #486: Run tests on AppVeyor 2016-03-29 23:39:53 -03:00
Pablo Santiago Blum de Aguiar
28153db4a8 #486: Ignore a test on Windows 2016-03-29 23:39:53 -03:00
Pablo Santiago Blum de Aguiar
047a1a6072 #486: Use Path instead of PosixPath 2016-03-29 23:39:53 -03:00
Pablo Santiago Blum de Aguiar
69db5c70e6 #486: Fix path joining on Windows 2016-03-29 23:39:45 -03:00
nvbn
fa1edd4bae Bump to 3.7 2016-03-23 05:12:29 +02:00
Vladimir Iakovlev
333c4b2a3f Merge pull request #483 from shakaran/patch-1
Update install procedure for pip
2016-03-23 05:11:07 +02:00
Vladimir Iakovlev
b1f10642fa Merge pull request #487 from scorphus/stdout-encoding-none
#486: Use alternative encoding when sys.stdout.encoding is None
2016-03-23 05:09:36 +02:00
Pablo Santiago Blum de Aguiar
047efd5575 #486: Use alternative encoding when sys.stdout.encoding is None
Fix #486
2016-03-22 16:13:59 -03:00
Vladimir Iakovlev
f604756cb7 Merge pull request #485 from scorphus/484-stdin-pipe
#484: Use PIPE as stdin when Popening the script
2016-03-21 22:35:18 +02:00
Pablo Santiago Blum de Aguiar
a27115bff1 #484: Use PIPE as stdin when Popening the script
Fix #484
2016-03-21 16:59:35 -03:00
Ángel Guzmán Maeso
5d00b3bc25 Update install procedure for pip
Avoid the warning:

The directory '/home/someuser/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
2016-03-21 17:48:41 +01:00
Vladimir Iakovlev
0cf4f5e8b0 Merge pull request #481 from scorphus/fix-git-add
Fix `git_add` rule
2016-03-19 12:59:22 +02:00
Pablo Santiago Blum de Aguiar
41707b80c6 #N/A: Fix git_add rule 2016-03-18 22:46:38 -03:00
Vladimir Iakovlev
3a39deb485 Merge pull request #478 from MattKotsenas/feature/powershell-shell
Add Powershell as shell
2016-03-19 02:45:26 +02:00
Matt Kotsenas
d4bc8cebf1 Replace raise with return for Ctrl+C in Windows
- Replace the raise `const.CtrlC` with `return const.CtrlC` the match the
  unix implementation and prevent a stacktrace when cancelling a command
  on Windows
2016-03-16 16:37:59 -07:00
Matt Kotsenas
6daf687237 Add Powershell as a supported shell
- There may be additional functionality to implement, but I've been
  running this way for a month with no known issues
2016-03-16 16:06:09 -07:00
Matt Kotsenas
2207dd2668 Update _get_shell to work with Windows
- _get_shell assumed the parent process would always be the shell process, in Powershell the
  parent process is Python, with the grandparent being the shell
- Switched to walking the process tree so the same code path can be used in both places
2016-03-15 14:10:04 -07:00
Vladimir Iakovlev
d1ab08a797 Merge pull request #477 from scorphus/git-rm-recursive
#N/A: Add new `git_rm_recursive` rule
2016-03-15 04:30:31 +03:00
Pablo Santiago Blum de Aguiar
51e89a36ef #N/A: Add new git_rm_recursive rule 2016-03-14 18:59:32 -03:00
nvbn
39f7cc37eb Bump to 3.6 2016-03-13 17:46:14 +03:00
nvbn
251b69b5a0 #475: Try to use already used executable in no_command 2016-03-13 15:10:37 +03:00
25 changed files with 359 additions and 123 deletions

View File

@@ -1,10 +1,10 @@
# The Fuck [![Build Status](https://travis-ci.org/nvbn/thefuck.svg?branch=master)](https://travis-ci.org/nvbn/thefuck)
# The Fuck [![Version][version-badge]][version-link] [![Build Status][travis-badge]][travis-link] [![Windows Build Status][appveyor-badge]][appveyor-link] [![Coverage][coverage-badge]][coverage-link] [![MIT License][license-badge]](LICENSE.md)
Magnificent app which corrects your previous console command,
inspired by a [@liamosaur](https://twitter.com/liamosaur/)
[tweet](https://twitter.com/liamosaur/status/506975850596536320).
[![gif with examples](https://raw.githubusercontent.com/nvbn/thefuck/master/example.gif)](https://raw.githubusercontent.com/nvbn/thefuck/master/example.gif)
[![gif with examples][examples-link]][examples-link]
Few more examples:
@@ -52,7 +52,7 @@ Python 3.4.2 (default, Oct 8 2014, 13:08:17)
git: 'brnch' is not a git command. See 'git --help'.
Did you mean this?
branch
branch
➜ fuck
git branch [enter/↑/↓/ctrl+c]
@@ -97,7 +97,7 @@ Reading package lists... Done
## Installation [*experimental*]
On Ubuntu and OS X you can install `The Fuck` with installation script:
```bash
wget -O - https://raw.githubusercontent.com/nvbn/thefuck/master/install.sh | sh - && $0
```
@@ -107,7 +107,7 @@ wget -O - https://raw.githubusercontent.com/nvbn/thefuck/master/install.sh | sh
Install `The Fuck` with `pip`:
```bash
sudo pip install thefuck
sudo -H pip install thefuck
```
[Or using an OS package manager (OS X, Ubuntu, Arch).](https://github.com/nvbn/thefuck/wiki/Installation)
@@ -155,8 +155,9 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `dry` – fixes repetitions like `git git push`;
* `fix_alt_space` – replaces Alt+Space with Space character;
* `fix_file` – opens a file with an error in your `$EDITOR`;
* `git_add` – fixes *"Did you forget to 'git add'?"*;
* `git_add` – fixes *"pathspec 'foo' did not match any file(s) known to git."*;
* `git_branch_delete` – changes `git branch -d` to `git branch -D`;
* `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_checkout` – fixes branch name or creates new branch;
* `git_diff_staged` – adds `--staged` to previous `git diff` with unexpected output;
@@ -167,6 +168,7 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `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`;
* `git_push_pull` – runs `git pull` when `push` was rejected;
* `git_rm_recursive` – adds `-r` when you try to `rm` a directory;
* `git_remote_seturl_add` – runs `git remote add` when `git remote set_url` on nonexistant remote;
* `git_stash` – stashes you local modifications before rebasing or switching branch;
* `git_two_dashes` – adds a missing dash to commands like `git commit -amend` or `git rebase -continue`;
@@ -305,7 +307,7 @@ debug = False
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_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_NO_COLORS` – disable colored output, `true/false`;
@@ -357,3 +359,15 @@ sudo apt-get install pandoc
## License MIT
Project License can be found [here](LICENSE.md).
[version-badge]: https://img.shields.io/pypi/v/thefuck.svg?label=version
[version-link]: https://pypi.python.org/pypi/thefuck/
[travis-badge]: https://img.shields.io/travis/nvbn/thefuck.svg
[travis-link]: https://travis-ci.org/nvbn/thefuck
[appveyor-badge]: https://img.shields.io/appveyor/ci/nvbn/thefuck.svg?label=windows%20build
[appveyor-link]: https://ci.appveyor.com/project/nvbn/thefuck
[coverage-badge]: https://img.shields.io/coveralls/nvbn/thefuck.svg
[coverage-link]: https://coveralls.io/github/nvbn/thefuck
[license-badge]: https://img.shields.io/badge/license-MIT-007EC7.svg
[examples-link]: https://raw.githubusercontent.com/nvbn/thefuck/master/example.gif

22
appveyor.yml Normal file
View File

@@ -0,0 +1,22 @@
build: false
environment:
matrix:
- PYTHON: "C:/Python27"
- PYTHON: "C:/Python33"
- PYTHON: "C:/Python34"
- PYTHON: "C:/Python35"
init:
- "ECHO %PYTHON%"
- ps: "ls C:/Python*"
install:
- ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py', 'C:/get-pip.py')
- "%PYTHON%/python.exe C:/get-pip.py"
- "%PYTHON%/Scripts/pip.exe install -U setuptools"
- "%PYTHON%/python.exe setup.py develop"
- "%PYTHON%/Scripts/pip.exe install -U -r requirements.txt"
test_script:
- "%PYTHON%/Scripts/py.test.exe -sv"

View File

@@ -26,7 +26,7 @@ elif (3, 0) < version < (3, 3):
' ({}.{} detected).'.format(*version))
sys.exit(-1)
VERSION = '3.5'
VERSION = '3.8'
install_requires = ['psutil', 'colorama', 'six', 'decorator']
extras_require = {':python_version<"3.4"': ['pathlib'],

View File

@@ -4,36 +4,28 @@ from tests.utils import Command
@pytest.fixture
def did_not_match(target, did_you_forget=True):
error = ("error: pathspec '{}' did not match any "
"file(s) known to git.".format(target))
if did_you_forget:
error = ("{}\nDid you forget to 'git add'?'".format(error))
return error
def stderr(target):
return ("error: pathspec '{}' did not match any "
'file(s) known to git.'.format(target))
@pytest.mark.parametrize('command', [
Command(script='git submodule update unknown',
stderr=did_not_match('unknown')),
Command(script='git commit unknown',
stderr=did_not_match('unknown'))]) # Older versions of Git
def test_match(command):
assert match(command)
@pytest.mark.parametrize('script, target', [
('git submodule update unknown', 'unknown'),
('git commit unknown', 'unknown')])
def test_match(stderr, script, target):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('command', [
Command(script='git submodule update known', stderr=('')),
Command(script='git commit known', stderr=('')),
Command(script='git commit unknown', # Newer versions of Git
stderr=did_not_match('unknown', False))])
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('script', [
'git submodule update known', 'git commit known'])
def test_not_match(script):
assert not match(Command(script=script, stderr=''))
@pytest.mark.parametrize('command, new_command', [
(Command('git submodule update unknown', stderr=did_not_match('unknown')),
@pytest.mark.parametrize('script, target, new_command', [
('git submodule update unknown', 'unknown',
'git add -- unknown && git submodule update unknown'),
(Command('git commit unknown', stderr=did_not_match('unknown')), # Old Git
('git commit unknown', 'unknown',
'git add -- unknown && git commit unknown')])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command
def test_get_new_command(stderr, script, target, new_command):
assert get_new_command(Command(script=script, stderr=stderr)) == new_command

View File

@@ -0,0 +1,33 @@
import pytest
from thefuck.rules.git_branch_exists import match, get_new_command
from tests.utils import Command
@pytest.fixture
def stderr(branch_name):
return "fatal: A branch named '{}' already exists.".format(branch_name)
@pytest.fixture
def new_command(branch_name):
return [cmd.format(branch_name) for cmd in [
'git branch -d {0} && git branch {0}',
'git branch -D {0} && git branch {0}', 'git checkout {0}']]
@pytest.mark.parametrize('script, branch_name', [
('git branch foo', 'foo'),
('git branch bar', 'bar')])
def test_match(stderr, script, branch_name):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('script', ['git branch foo', 'git branch bar'])
def test_not_match(script):
assert not match(Command(script=script, stderr=''))
@pytest.mark.parametrize('script, branch_name, ', [
('git branch foo', 'foo'), ('git branch bar', 'bar')])
def test_get_new_command(stderr, new_command, script, branch_name):
assert get_new_command(Command(script=script, stderr=stderr)) == new_command

View File

@@ -0,0 +1,27 @@
import pytest
from thefuck.rules.git_rm_recursive import match, get_new_command
from tests.utils import Command
@pytest.fixture
def stderr(target):
return "fatal: not removing '{}' recursively without -r".format(target)
@pytest.mark.parametrize('script, target', [
('git rm foo', 'foo'),
('git rm foo bar', 'foo bar')])
def test_match(stderr, script, target):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('script', ['git rm foo', 'git rm foo bar'])
def test_not_match(script):
assert not match(Command(script=script, stderr=''))
@pytest.mark.parametrize('script, target, new_command', [
('git rm foo', 'foo', 'git rm -r foo'),
('git rm foo bar', 'foo bar', 'git rm -r foo bar')])
def test_get_new_command(stderr, script, target, new_command):
assert get_new_command(Command(script=script, stderr=stderr)) == new_command

View File

@@ -3,38 +3,23 @@ from thefuck.rules.history import match, get_new_command
from tests.utils import Command
@pytest.fixture
def history(mocker):
return mocker.patch('thefuck.shells.shell.get_history',
return_value=['le cat', 'fuck', 'ls cat',
'diff x', 'nocommand x'])
@pytest.fixture(autouse=True)
def history_without_current(mocker):
return mocker.patch(
'thefuck.rules.history.get_valid_history_without_current',
return_value=['ls cat', 'diff x'])
@pytest.fixture
def alias(mocker):
return mocker.patch('thefuck.rules.history.get_alias',
return_value='fuck')
@pytest.fixture
def callables(mocker):
return mocker.patch('thefuck.rules.history.get_all_executables',
return_value=['diff', 'ls'])
@pytest.mark.usefixtures('history', 'callables', 'no_memoize', 'alias')
@pytest.mark.parametrize('script', ['ls cet', 'daff x'])
def test_match(script):
assert match(Command(script=script))
@pytest.mark.usefixtures('history', 'callables', 'no_memoize', 'alias')
@pytest.mark.parametrize('script', ['apt-get', 'nocommand y'])
def test_not_match(script):
assert not match(Command(script=script))
@pytest.mark.usefixtures('history', 'callables', 'no_memoize', 'alias')
@pytest.mark.parametrize('script, result', [
('ls cet', 'ls cat'),
('daff x', 'diff x')])

View File

@@ -6,22 +6,37 @@ from tests.utils import Command
@pytest.fixture(autouse=True)
def get_all_executables(mocker):
mocker.patch('thefuck.rules.no_command.get_all_executables',
return_value=['vim', 'apt-get', 'fsck'])
return_value=['vim', 'fsck', 'git', 'go'])
@pytest.fixture(autouse=True)
def history_without_current(mocker):
return mocker.patch(
'thefuck.rules.no_command.get_valid_history_without_current',
return_value=['git commit'])
@pytest.mark.usefixtures('no_memoize')
def test_match():
assert match(Command(stderr='vom: not found', script='vom file.py'))
assert match(Command(stderr='fucck: not found', script='fucck'))
assert not match(Command(stderr='qweqwe: not found', script='qweqwe'))
assert not match(Command(stderr='some text', script='vom file.py'))
@pytest.mark.parametrize('script, stderr', [
('vom file.py', 'vom: not found'),
('fucck', 'fucck: not found'),
('got commit', 'got: command not found')])
def test_match(script, stderr):
assert match(Command(script, stderr=stderr))
@pytest.mark.usefixtures('no_memoize')
def test_get_new_command():
assert get_new_command(
Command(stderr='vom: not found',
script='vom file.py')) == ['vim file.py']
assert get_new_command(
Command(stderr='fucck: not found',
script='fucck')) == ['fsck']
@pytest.mark.parametrize('script, stderr', [
('qweqwe', 'qweqwe: not found'),
('vom file.py', 'some text')])
def test_not_match(script, stderr):
assert not match(Command(script, stderr=stderr))
@pytest.mark.usefixtures('no_memoize')
@pytest.mark.parametrize('script, result', [
('vom file.py', ['vim file.py']),
('fucck', ['fsck']),
('got commit', ['git commit', 'go commit'])])
def test_get_new_command(script, result):
assert get_new_command(Command(script)) == result

View File

@@ -52,6 +52,7 @@ def test_match(ssh_error):
assert not match(Command('ssh'))
@pytest.mark.skipif(os.name == 'nt', reason='Skip if testing on Windows')
def test_side_effect(ssh_error):
errormsg, path, reset, known_hosts = ssh_error
command = Command('ssh user@host', stderr=errormsg)

View File

@@ -19,14 +19,18 @@ class TestFish(object):
return mock
@pytest.fixture
def environ(self, monkeypatch):
data = {'TF_OVERRIDDEN_ALIASES': 'cd, ls, man, open'}
monkeypatch.setattr('thefuck.shells.fish.os.environ', data)
return data
def os_environ(self, monkeypatch, key, value):
monkeypatch.setattr('os.environ', {key: value})
@pytest.mark.usefixture('environ')
def test_get_overridden_aliases(self, shell, environ):
assert shell._get_overridden_aliases() == ['cd', 'ls', 'man', 'open']
@pytest.mark.parametrize('key, value', [
('TF_OVERRIDDEN_ALIASES', 'cut,git,sed'), # legacy
('THEFUCK_OVERRIDDEN_ALIASES', 'cut,git,sed'),
('THEFUCK_OVERRIDDEN_ALIASES', 'cut, git, sed'),
('THEFUCK_OVERRIDDEN_ALIASES', ' cut,\tgit,sed\n'),
('THEFUCK_OVERRIDDEN_ALIASES', '\ncut,\n\ngit,\tsed\r')])
def test_get_overridden_aliases(self, shell, os_environ):
assert shell._get_overridden_aliases() == {'cd', 'cut', 'git', 'grep',
'ls', 'man', 'open', 'sed'}
@pytest.mark.parametrize('before, after', [
('cd', 'cd'),

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
import pytest
from thefuck.shells import Powershell
@pytest.mark.usefixtures('isfile', 'no_memoize', 'no_cache')
class TestPowershell(object):
@pytest.fixture
def shell(self):
return Powershell()
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == '(ls) -and (cd)'
def test_app_alias(self, shell):
assert 'function fuck' in shell.app_alias('fuck')
assert 'function FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
import pytest
from pathlib import PosixPath
from pathlib import Path
from thefuck import corrector, const
from tests.utils import Rule, Command, CorrectedCommand
from thefuck.corrector import get_corrected_commands, organize_commands
@@ -30,7 +30,7 @@ class TestGetRules(object):
(['git.py', 'bash.py'], ['git'], ['git'], [])])
def test_get_rules(self, glob, settings, paths, conf_rules, exclude_rules,
loaded_rules):
glob([PosixPath(path) for path in paths])
glob([Path(path) for path in paths])
settings.update(rules=conf_rules,
priority={},
exclude_rules=exclude_rules)

View File

@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import os
from subprocess import PIPE
from mock import Mock
from pathlib import Path
@@ -39,9 +40,10 @@ class TestRule(object):
enabled_by_default=True,
priority=900,
requires_output=True))
assert Rule.from_path(Path('/rules/bash.py')) \
rule_path = os.path.join(os.sep, 'rules', 'bash.py')
assert Rule.from_path(Path(rule_path)) \
== Rule('bash', match, get_new_command, priority=900)
load_source.assert_called_once_with('bash', '/rules/bash.py')
load_source.assert_called_once_with('bash', rule_path)
@pytest.mark.parametrize('rules, exclude_rules, rule, is_enabled', [
(const.DEFAULT_RULES, [], Rule('git', enabled_by_default=True), True),
@@ -110,6 +112,7 @@ class TestCommand(object):
'apt-get search vim', 'stdout', 'stderr')
Popen.assert_called_once_with('apt-get search vim',
shell=True,
stdin=PIPE,
stdout=PIPE,
stderr=PIPE,
env={})

View File

@@ -3,7 +3,8 @@ from mock import Mock
import six
from thefuck.utils import default_settings, \
memoize, get_closest, get_all_executables, replace_argument, \
get_all_matched_commands, is_app, for_app, cache, compatibility_call
get_all_matched_commands, is_app, for_app, cache, compatibility_call, \
get_valid_history_without_current
from tests.utils import Command
@@ -229,3 +230,29 @@ class TestCompatibilityCall(object):
return True
assert compatibility_call(side_effect, Command(), Command())
class TestGetValidHistoryWithoutCurrent(object):
@pytest.fixture(autouse=True)
def history(self, mocker):
return mocker.patch('thefuck.shells.shell.get_history',
return_value=['le cat', 'fuck', 'ls cat',
'diff x', 'nocommand x'])
@pytest.fixture(autouse=True)
def alias(self, mocker):
return mocker.patch('thefuck.utils.get_alias',
return_value='fuck')
@pytest.fixture(autouse=True)
def callables(self, mocker):
return mocker.patch('thefuck.utils.get_all_executables',
return_value=['diff', 'ls'])
@pytest.mark.parametrize('script, result', [
('le cat', ['ls cat', 'diff x']),
('diff x', ['ls cat']),
('fuck', ['ls cat', 'diff x'])])
def test_get_valid_history_without_current(self, script, result):
command = Command(script=script)
assert get_valid_history_without_current(command) == result

View File

@@ -5,15 +5,14 @@ from thefuck.specific.git import git_support
@git_support
def match(command):
return ('did not match any file(s) known to git.' in command.stderr
and "Did you forget to 'git add'?" in command.stderr)
return 'did not match any file(s) known to git.' in command.stderr
@git_support
def get_new_command(command):
missing_file = re.findall(
r"error: pathspec '([^']*)' "
r"did not match any file\(s\) known to git.", command.stderr)[0]
r"error: pathspec '([^']*)' "
r'did not match any file\(s\) known to git.', command.stderr)[0]
formatme = shell.and_('git add -- {}', '{}')
return formatme.format(missing_file, command.script)

View File

@@ -0,0 +1,23 @@
import re
from thefuck.shells import shell
from thefuck.specific.git import git_support
from thefuck.utils import eager
@git_support
def match(command):
return ('branch' in command.script
and "fatal: A branch named '" in command.stderr
and " already exists." in command.stderr)
@git_support
@eager
def get_new_command(command):
branch_name = re.findall(
r"fatal: A branch named '([^']*)' already exists.", command.stderr)[0]
new_command_templates = [['git branch -d {0}', 'git branch {0}'],
['git branch -D {0}', 'git branch {0}'],
['git checkout {0}']]
for new_command_template in new_command_templates:
yield shell.and_(*new_command_template).format(branch_name)

View File

@@ -0,0 +1,15 @@
from thefuck.specific.git import git_support
@git_support
def match(command):
return (' rm ' in command.script
and "fatal: not removing '" in command.stderr
and "' recursively without -r" in command.stderr)
@git_support
def get_new_command(command):
index = command.script_parts.index('rm') + 1
command.script_parts.insert(index, '-r')
return u' '.join(command.script_parts)

View File

@@ -1,37 +1,15 @@
from difflib import get_close_matches
from thefuck.shells import shell
from thefuck.utils import get_closest, memoize, get_all_executables, get_alias
def _not_corrected(history, tf_alias):
"""Returns all lines from history except that comes before `fuck`."""
previous = None
for line in history:
if previous is not None and line != tf_alias:
yield previous
previous = line
if history:
yield history[-1]
@memoize
def _history_of_exists_without_current(command):
history = shell.get_history()
tf_alias = get_alias()
executables = get_all_executables()
return [line for line in _not_corrected(history, tf_alias)
if not line.startswith(tf_alias) and not line == command.script
and line.split(' ')[0] in executables]
from thefuck.utils import get_closest, get_valid_history_without_current
def match(command):
return len(get_close_matches(command.script,
_history_of_exists_without_current(command)))
get_valid_history_without_current(command)))
def get_new_command(command):
return get_closest(command.script,
_history_of_exists_without_current(command))
get_valid_history_without_current(command))
priority = 9999

View File

@@ -1,5 +1,6 @@
from difflib import get_close_matches
from thefuck.utils import get_all_executables
from thefuck.utils import get_all_executables, \
get_valid_history_without_current, get_closest
from thefuck.specific.sudo import sudo_support
@@ -11,10 +12,29 @@ def match(command):
get_all_executables())))
def _get_used_executables(command):
for script in get_valid_history_without_current(command):
yield script.split(' ')[0]
@sudo_support
def get_new_command(command):
old_command = command.script_parts[0]
new_cmds = get_close_matches(old_command, get_all_executables(), cutoff=0.1)
# One from history:
already_used = get_closest(
old_command, _get_used_executables(command),
fallback_to_first=False)
if already_used:
new_cmds = [already_used]
else:
new_cmds = []
# Other from all executables:
new_cmds += [cmd for cmd in get_close_matches(old_command,
get_all_executables())
if cmd not in new_cmds]
return [' '.join([new_command] + command.script_parts[1:])
for new_command in new_cmds]

View File

@@ -9,20 +9,37 @@ from .fish import Fish
from .generic import Generic
from .tcsh import Tcsh
from .zsh import Zsh
from .powershell import Powershell
shells = {'bash': Bash,
'fish': Fish,
'zsh': Zsh,
'csh': Tcsh,
'tcsh': Tcsh}
'tcsh': Tcsh,
'powershell': Powershell}
def _get_shell():
try:
shell_name = Process(os.getpid()).parent().name()
except TypeError:
shell_name = Process(os.getpid()).parent.name
return shells.get(shell_name, Generic)()
proc = Process(os.getpid())
while (proc is not None):
name = None
try:
name = proc.name()
except TypeError:
name = proc.name
name = os.path.splitext(name)[0]
if name in shells:
return shells[name]()
try:
proc = proc.parent()
except TypeError:
proc = proc.parent
return Generic()
shell = _get_shell()

View File

@@ -7,11 +7,12 @@ from .generic import Generic
class Fish(Generic):
def _get_overridden_aliases(self):
overridden_aliases = os.environ.get('TF_OVERRIDDEN_ALIASES', '').strip()
if overridden_aliases:
return [alias.strip() for alias in overridden_aliases.split(',')]
else:
return ['cd', 'grep', 'ls', 'man', 'open']
overridden = os.environ.get('THEFUCK_OVERRIDDEN_ALIASES',
os.environ.get('TF_OVERRIDDEN_ALIASES', ''))
default = {'cd', 'grep', 'ls', 'man', 'open'}
for alias in overridden.split(','):
default.add(alias.strip())
return default
def app_alias(self, fuck):
# It is VERY important to have the variables declared WITHIN the alias

View File

@@ -0,0 +1,18 @@
from .generic import Generic
class Powershell(Generic):
def app_alias(self, fuck):
return 'function ' + fuck + ' { \n' \
' $fuck = $(thefuck (Get-History -Count 1).CommandLine)\n' \
' if (-not [string]::IsNullOrWhiteSpace($fuck)) {\n' \
' if ($fuck.StartsWith("echo")) { $fuck = $fuck.Substring(5) }\n' \
' else { iex "$fuck" }\n' \
' }\n' \
'}\n'
def and_(self, *commands):
return u' -and '.join('({0})'.format(c) for c in commands)
def how_to_configure(self):
return 'iex "thefuck --alias"', '$profile'

View File

@@ -1,3 +1,4 @@
import os
import sys
import msvcrt
import win_unicode_console
@@ -16,10 +17,11 @@ def get_key():
ch = msvcrt.getch() # second call returns the actual key code
if ch == b'\x03':
raise const.KEY_CTRL_C
return const.KEY_CTRL_C
if ch == b'H':
return const.KEY_UP
if ch == b'P':
return const.KEY_DOWN
return ch.decode(sys.stdout.encoding)
encoding = sys.stdout.encoding or os.environ.get('PYTHONIOENCODING', 'utf-8')
return ch.decode(encoding)

View File

@@ -114,7 +114,7 @@ class Command(object):
env.update(settings.env)
with logs.debug_time(u'Call: {}; with env: {};'.format(script, env)):
result = Popen(script, shell=True, stdout=PIPE, stderr=PIPE, env=env)
result = Popen(script, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=env)
if cls._wait_output(result):
stdout = result.stdout.read().decode('utf-8')
stderr = result.stderr.read().decode('utf-8')

View File

@@ -269,3 +269,24 @@ def get_installation_info():
def get_alias():
return os.environ.get('TF_ALIAS', 'fuck')
@memoize
def get_valid_history_without_current(command):
def _not_corrected(history, tf_alias):
"""Returns all lines from history except that comes before `fuck`."""
previous = None
for line in history:
if previous is not None and line != tf_alias:
yield previous
previous = line
if history:
yield history[-1]
from thefuck.shells import shell
history = shell.get_history()
tf_alias = get_alias()
executables = get_all_executables()
return [line for line in _not_corrected(history, tf_alias)
if not line.startswith(tf_alias) and not line == command.script
and line.split(' ')[0] in executables]