From f966ecd4f5b8221ee15e843f5ec287e1f7cca940 Mon Sep 17 00:00:00 2001 From: Joseph Frazier <1212jtraceur@gmail.com> Date: Wed, 20 Dec 2017 20:43:01 -0500 Subject: [PATCH 01/23] Use pytest<3.3 to fix Python 3.3 tests (#746) See https://github.com/nvbn/thefuck/pull/744 for context. I'm personally okay with dropping Python 3.3 support, but I'd like to at least get the tests working while we decide on that. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c450e43..e3d6b26 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pip flake8 -pytest +pytest<3.3 mock pytest-mock wheel From 4ea02a31537eebbb716e4ece145b252101b4f8b2 Mon Sep 17 00:00:00 2001 From: Joseph Frazier <1212jtraceur@gmail.com> Date: Wed, 27 Dec 2017 07:54:52 -0500 Subject: [PATCH 02/23] git_push: Don't add duplicate remote/branch name (#745) This fixes https://github.com/nvbn/thefuck/issues/740 --- tests/rules/test_git_push.py | 2 ++ thefuck/rules/git_push.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/tests/rules/test_git_push.py b/tests/rules/test_git_push.py index debd703..c654b2a 100644 --- a/tests/rules/test_git_push.py +++ b/tests/rules/test_git_push.py @@ -23,6 +23,8 @@ def test_match(output): def test_get_new_command(output): assert get_new_command(Command('git push', output))\ == "git push --set-upstream origin master" + assert get_new_command(Command('git push master', output))\ + == "git push --set-upstream origin master" assert get_new_command(Command('git push -u', output))\ == "git push --set-upstream origin master" assert get_new_command(Command('git push -u origin', output))\ diff --git a/thefuck/rules/git_push.py b/thefuck/rules/git_push.py index 7a47240..c5f49a5 100644 --- a/thefuck/rules/git_push.py +++ b/thefuck/rules/git_push.py @@ -32,6 +32,10 @@ def get_new_command(command): # In case of `git push -u` we don't have next argument: if len(command_parts) > upstream_option_index: command_parts.pop(upstream_option_index) + # If the only argument is the remote/branch, remove it. + # git's suggestion includes it, so it won't be lost, and we would duplicate it otherwise. + elif len(command_parts) is 3 and command_parts[2][0] != '-': + command_parts.pop(2) arguments = re.findall(r'git push (.*)', command.output)[0].strip() return replace_argument(" ".join(command_parts), 'push', From 9e788196e6feeddc9b87b7db1af5c91abd7dbc55 Mon Sep 17 00:00:00 2001 From: Miguel Piedrafita Date: Mon, 1 Jan 2018 18:53:34 +0100 Subject: [PATCH 03/23] Update license year to 2015-2018 (#752) --- LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.md b/LICENSE.md index d79db85..ec1d1e2 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,7 +1,7 @@ The MIT License (MIT) ===================== -Copyright (c) 2015 Vladimir Iakovlev +Copyright (c) 2015-2018 Vladimir Iakovlev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 83cf97dc26f846d1d9d782e4340692df3be7343f Mon Sep 17 00:00:00 2001 From: David Hart Date: Mon, 1 Jan 2018 23:30:33 +0000 Subject: [PATCH 04/23] Suggest git checkout -b (#754) This fixes https://github.com/nvbn/thefuck/issues/632 This uses `script_parts` instead of `script.startswith` to let it work even if there's extra spaces in the command, e.g. git checkout unknown --- tests/rules/test_git_checkout.py | 2 +- thefuck/rules/git_checkout.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/rules/test_git_checkout.py b/tests/rules/test_git_checkout.py index 355b686..a803f20 100644 --- a/tests/rules/test_git_checkout.py +++ b/tests/rules/test_git_checkout.py @@ -52,7 +52,7 @@ def test_get_branches(branches, branch_list, git_branch): @pytest.mark.parametrize('branches, command, new_command', [ (b'', Command('git checkout unknown', did_not_match('unknown')), - 'git branch unknown && git checkout unknown'), + 'git checkout -b unknown'), (b'', Command('git commit unknown', did_not_match('unknown')), 'git branch unknown && git commit unknown'), diff --git a/thefuck/rules/git_checkout.py b/thefuck/rules/git_checkout.py index 934f0a4..65705f4 100644 --- a/thefuck/rules/git_checkout.py +++ b/thefuck/rules/git_checkout.py @@ -34,6 +34,8 @@ def get_new_command(command): fallback_to_first=False) if closest_branch: return replace_argument(command.script, missing_file, closest_branch) + elif command.script_parts[1] == 'checkout': + return replace_argument(command.script, 'checkout', 'checkout -b') else: return shell.and_('git branch {}', '{}').format( missing_file, command.script) From 57fb6e079a6430804cf03347dcb679be5266a133 Mon Sep 17 00:00:00 2001 From: David Hart Date: Tue, 2 Jan 2018 00:45:46 +0000 Subject: [PATCH 05/23] git_push: Make option handling more robust (#751) See https://github.com/nvbn/thefuck/issues/740#issuecomment-354466567 --- tests/rules/test_git_push.py | 6 ++++++ thefuck/rules/git_push.py | 10 ++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/rules/test_git_push.py b/tests/rules/test_git_push.py index c654b2a..785b08e 100644 --- a/tests/rules/test_git_push.py +++ b/tests/rules/test_git_push.py @@ -29,7 +29,13 @@ def test_get_new_command(output): == "git push --set-upstream origin master" assert get_new_command(Command('git push -u origin', output))\ == "git push --set-upstream origin master" + assert get_new_command(Command('git push origin', output))\ + == "git push --set-upstream origin master" assert get_new_command(Command('git push --set-upstream origin', output))\ == "git push --set-upstream origin master" assert get_new_command(Command('git push --quiet', output))\ == "git push --set-upstream origin master --quiet" + assert get_new_command(Command('git push --quiet origin', output))\ + == "git push --set-upstream origin master --quiet" + assert get_new_command(Command('git -c test=test push --quiet origin', output))\ + == "git -c test=test push --set-upstream origin master --quiet" diff --git a/thefuck/rules/git_push.py b/thefuck/rules/git_push.py index c5f49a5..e11938b 100644 --- a/thefuck/rules/git_push.py +++ b/thefuck/rules/git_push.py @@ -32,10 +32,12 @@ def get_new_command(command): # In case of `git push -u` we don't have next argument: if len(command_parts) > upstream_option_index: command_parts.pop(upstream_option_index) - # If the only argument is the remote/branch, remove it. - # git's suggestion includes it, so it won't be lost, and we would duplicate it otherwise. - elif len(command_parts) is 3 and command_parts[2][0] != '-': - command_parts.pop(2) + else: + # the only non-qualified permitted options are the repository and refspec; git's + # suggestion include them, so they won't be lost, but would be duplicated otherwise. + push_idx = command_parts.index('push') + 1 + while len(command_parts) > push_idx and command_parts[len(command_parts) - 1][0] != '-': + command_parts.pop(len(command_parts) - 1) arguments = re.findall(r'git push (.*)', command.output)[0].strip() return replace_argument(" ".join(command_parts), 'push', From 064050989501179ff6c4bff059aa7db4d384a567 Mon Sep 17 00:00:00 2001 From: Joseph Frazier <1212jtraceur@gmail.com> Date: Mon, 1 Jan 2018 20:18:05 -0500 Subject: [PATCH 06/23] Drop Python 3.3 Support (#747) * Drop Python 3.3 Support It's reached end-of-life, and our dependencies have started to drop it. See https://github.com/nvbn/thefuck/pull/744#issuecomment-353244371 * Revert "Use pytest<3.3 to fix Python 3.3 tests (#746)" This reverts commit f966ecd4f5b8221ee15e843f5ec287e1f7cca940. --- .travis.yml | 3 --- README.md | 2 +- appveyor.yml | 1 - requirements.txt | 2 +- setup.py | 4 ++-- tox.ini | 2 +- 6 files changed, 5 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 990a7f0..3bc2c4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,9 +11,6 @@ matrix: - os: linux dist: trusty python: "3.4" - - os: linux - dist: trusty - python: "3.3" - os: linux dist: trusty python: "2.7" diff --git a/README.md b/README.md index 94fff46..a389f69 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ Reading package lists... Done ## Requirements -- python (3.3+) +- python (3.4+) - pip - python-dev diff --git a/appveyor.yml b/appveyor.yml index da1711d..541a69e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,7 +3,6 @@ build: false environment: matrix: - PYTHON: "C:/Python27" - - PYTHON: "C:/Python33" - PYTHON: "C:/Python34" - PYTHON: "C:/Python35" - PYTHON: "C:/Python36" diff --git a/requirements.txt b/requirements.txt index e3d6b26..c450e43 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ pip flake8 -pytest<3.3 +pytest mock pytest-mock wheel diff --git a/setup.py b/setup.py index 69d4aac..6ace6ef 100755 --- a/setup.py +++ b/setup.py @@ -26,8 +26,8 @@ if version < (2, 7): print('thefuck requires Python version 2.7 or later' + ' ({}.{} detected).'.format(*version)) sys.exit(-1) -elif (3, 0) < version < (3, 3): - print('thefuck requires Python version 3.3 or later' + +elif (3, 0) < version < (3, 4): + print('thefuck requires Python version 3.4 or later' + ' ({}.{} detected).'.format(*version)) sys.exit(-1) diff --git a/tox.ini b/tox.ini index 8fc3820..f299475 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py33,py34,py35,py36 +envlist = py27,py34,py35,py36 [testenv] deps = -rrequirements.txt From 897572d27880ece05213a323dbc0499d669cc407 Mon Sep 17 00:00:00 2001 From: Joseph Frazier <1212jtraceur@gmail.com> Date: Tue, 2 Jan 2018 10:14:11 -0500 Subject: [PATCH 07/23] README: Use `pip3` in upgrade command (#756) Fixes https://github.com/nvbn/thefuck/issues/615 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a389f69..26ba5ab 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ fuck -r ## Update ```bash -pip install thefuck --upgrade +pip3 install thefuck --upgrade ``` **Aliases changed in 1.34.** From f700b23f5725e14f8ee6ffb0a5c44ab9eaf42b53 Mon Sep 17 00:00:00 2001 From: David Hart Date: Tue, 2 Jan 2018 16:47:48 +0000 Subject: [PATCH 08/23] Add git merge rule (#755) This fixes https://github.com/nvbn/thefuck/issues/629 --- README.md | 1 + tests/rules/test_git_merge.py | 26 ++++++++++++++++++++++++++ thefuck/rules/git_merge.py | 18 ++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 tests/rules/test_git_merge.py create mode 100644 thefuck/rules/git_merge.py diff --git a/README.md b/README.md index 26ba5ab..e686d06 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,7 @@ using the matched rule and runs it. Rules enabled by default are as follows: * `git_fix_stash` – fixes `git stash` commands (misspelled subcommand and missing `save`); * `git_flag_after_filename` – fixes `fatal: bad flag '...' after filename` * `git_help_aliased` – fixes `git help ` commands replacing with the aliased command; +* `git_merge` – adds remote to branch names; * `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; diff --git a/tests/rules/test_git_merge.py b/tests/rules/test_git_merge.py new file mode 100644 index 0000000..c347a59 --- /dev/null +++ b/tests/rules/test_git_merge.py @@ -0,0 +1,26 @@ +import pytest +from thefuck.rules.git_merge import match, get_new_command +from thefuck.types import Command + + +@pytest.fixture +def output(): + return 'merge: local - not something we can merge\n\n' \ + 'Did you mean this?\n\tremote/local' + + +def test_match(output): + assert match(Command('git merge test', output)) + assert not match(Command('git merge master', '')) + assert not match(Command('ls', output)) + + +@pytest.mark.parametrize('command, new_command', [ + (Command('git merge local', output()), + 'git merge remote/local'), + (Command('git merge -m "test" local', output()), + 'git merge -m "test" remote/local'), + (Command('git merge -m "test local" local', output()), + 'git merge -m "test local" remote/local')]) +def test_get_new_command(command, new_command): + assert get_new_command(command) == new_command diff --git a/thefuck/rules/git_merge.py b/thefuck/rules/git_merge.py new file mode 100644 index 0000000..2420b67 --- /dev/null +++ b/thefuck/rules/git_merge.py @@ -0,0 +1,18 @@ +import re +from thefuck.utils import replace_argument +from thefuck.specific.git import git_support + + +@git_support +def match(command): + return ('merge' in command.script + and ' - not something we can merge' in command.output + and 'Did you mean this?' in command.output) + + +@git_support +def get_new_command(command): + unknown_branch = re.findall(r'merge: (.+) - not something we can merge', command.output)[0] + remote_branch = re.findall(r'Did you mean this\?\n\t([^\n]+)', command.output)[0] + + return replace_argument(command.script, unknown_branch, remote_branch) From bcb749722b409a4148b642374bf8ca55bae3598d Mon Sep 17 00:00:00 2001 From: Pablo Santiago Blum de Aguiar Date: Sat, 16 Dec 2017 22:46:44 -0200 Subject: [PATCH 09/23] #738: Set SHELL env var for fish and tcsh --- tests/shells/test_fish.py | 1 + tests/shells/test_tcsh.py | 1 + thefuck/shells/fish.py | 2 +- thefuck/shells/tcsh.py | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/shells/test_fish.py b/tests/shells/test_fish.py index 836a648..dacc018 100644 --- a/tests/shells/test_fish.py +++ b/tests/shells/test_fish.py @@ -71,6 +71,7 @@ class TestFish(object): assert 'function fuck' in shell.app_alias('fuck') assert 'function FUCK' in shell.app_alias('FUCK') assert 'thefuck' in shell.app_alias('fuck') + assert 'TF_SHELL=fish' in shell.app_alias('fuck') assert 'TF_ALIAS=fuck PYTHONIOENCODING' in shell.app_alias('fuck') assert 'PYTHONIOENCODING=utf-8 thefuck' in shell.app_alias('fuck') diff --git a/tests/shells/test_tcsh.py b/tests/shells/test_tcsh.py index 7bc8ae3..014c3c9 100644 --- a/tests/shells/test_tcsh.py +++ b/tests/shells/test_tcsh.py @@ -44,6 +44,7 @@ class TestTcsh(object): 'll': 'ls -alF'} def test_app_alias(self, shell): + assert 'setenv TF_SHELL tcsh' in shell.app_alias('fuck') assert 'alias fuck' in shell.app_alias('fuck') assert 'alias FUCK' in shell.app_alias('FUCK') assert 'thefuck' in shell.app_alias('fuck') diff --git a/thefuck/shells/fish.py b/thefuck/shells/fish.py index 56ad214..0b635db 100644 --- a/thefuck/shells/fish.py +++ b/thefuck/shells/fish.py @@ -35,7 +35,7 @@ class Fish(Generic): # It is VERY important to have the variables declared WITHIN the alias return ('function {0} -d "Correct your previous console command"\n' ' set -l fucked_up_command $history[1]\n' - ' env TF_ALIAS={0} PYTHONIOENCODING=utf-8' + ' env TF_SHELL=fish TF_ALIAS={0} PYTHONIOENCODING=utf-8' ' thefuck $fucked_up_command | read -l unfucked_command\n' ' if [ "$unfucked_command" != "" ]\n' ' eval $unfucked_command\n{1}' diff --git a/thefuck/shells/tcsh.py b/thefuck/shells/tcsh.py index d8470eb..0911f04 100644 --- a/thefuck/shells/tcsh.py +++ b/thefuck/shells/tcsh.py @@ -7,7 +7,7 @@ from .generic import Generic class Tcsh(Generic): def app_alias(self, alias_name): - return ("alias {0} 'setenv TF_ALIAS {0} && " + return ("alias {0} 'setenv TF_SHELL tcsh && setenv TF_ALIAS {0} && " "set fucked_cmd=`history -h 2 | head -n 1` && " "eval `thefuck ${{fucked_cmd}}`'").format(alias_name) From 045c8ae76c9a020d3d27ab132cd06fa075b20f7d Mon Sep 17 00:00:00 2001 From: Pablo Santiago Blum de Aguiar Date: Sun, 17 Dec 2017 18:01:40 -0200 Subject: [PATCH 10/23] #738: Assert TF_SHELL is defined in bash and zsh aliases --- tests/shells/test_bash.py | 1 + tests/shells/test_zsh.py | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/shells/test_bash.py b/tests/shells/test_bash.py index 117e3af..b10ae0e 100644 --- a/tests/shells/test_bash.py +++ b/tests/shells/test_bash.py @@ -51,6 +51,7 @@ class TestBash(object): def test_app_alias_variables_correctly_set(self, shell): alias = shell.app_alias('fuck') assert "fuck () {" in alias + assert 'TF_SHELL=bash' in alias assert "TF_ALIAS=fuck" in alias assert 'PYTHONIOENCODING=utf-8' in alias assert 'TF_SHELL_ALIASES=$(alias)' in alias diff --git a/tests/shells/test_zsh.py b/tests/shells/test_zsh.py index e31c362..fc20a06 100644 --- a/tests/shells/test_zsh.py +++ b/tests/shells/test_zsh.py @@ -51,6 +51,7 @@ class TestZsh(object): def test_app_alias_variables_correctly_set(self, shell): alias = shell.app_alias('fuck') assert "fuck () {" in alias + assert 'TF_SHELL=zsh' in alias assert "TF_ALIAS=fuck" in alias assert 'PYTHONIOENCODING=utf-8' in alias assert 'TF_SHELL_ALIASES=$(alias)' in alias From 83e1710712a9b48fee1822109a10a8324fe64930 Mon Sep 17 00:00:00 2001 From: David Hart Date: Wed, 3 Jan 2018 04:14:02 +0000 Subject: [PATCH 11/23] Fix fish shell aliasing (#753) * Handle user defined fish aliases * Add more aliases to test * Revert unecessary Popen mock changes * Add test for fish aliasing Fixes #727 --- tests/shells/test_fish.py | 12 ++++++++---- thefuck/shells/fish.py | 23 ++++++++++++++++++++--- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/tests/shells/test_fish.py b/tests/shells/test_fish.py index dacc018..efd684f 100644 --- a/tests/shells/test_fish.py +++ b/tests/shells/test_fish.py @@ -13,9 +13,10 @@ class TestFish(object): @pytest.fixture(autouse=True) def Popen(self, mocker): mock = mocker.patch('thefuck.shells.fish.Popen') - mock.return_value.stdout.read.return_value = ( + mock.return_value.stdout.read.side_effect = [( b'cd\nfish_config\nfuck\nfunced\nfuncsave\ngrep\nhistory\nll\nls\n' - b'man\nmath\npopd\npushd\nruby') + b'man\nmath\npopd\npushd\nruby'), + b'alias fish_key_reader /usr/bin/fish_key_reader\nalias g git'] return mock @pytest.mark.parametrize('key, value', [ @@ -42,7 +43,8 @@ class TestFish(object): ('open', 'open'), ('vim', 'vim'), ('ll', 'fish -ic "ll"'), - ('ls', 'ls')]) # Fish has no aliases but functions + ('ls', 'ls'), + ('g', 'git')]) def test_from_shell(self, before, after, shell): assert shell.from_shell(before) == after @@ -65,7 +67,9 @@ class TestFish(object): 'math': 'math', 'popd': 'popd', 'pushd': 'pushd', - 'ruby': 'ruby'} + 'ruby': 'ruby', + 'g': 'git', + 'fish_key_reader': '/usr/bin/fish_key_reader'} def test_app_alias(self, shell): assert 'function fuck' in shell.app_alias('fuck') diff --git a/thefuck/shells/fish.py b/thefuck/shells/fish.py index 0b635db..471df2a 100644 --- a/thefuck/shells/fish.py +++ b/thefuck/shells/fish.py @@ -10,12 +10,24 @@ from .generic import Generic @cache('~/.config/fish/config.fish', '~/.config/fish/functions') -def _get_aliases(overridden): +def _get_functions(overridden): proc = Popen(['fish', '-ic', 'functions'], stdout=PIPE, stderr=DEVNULL) functions = proc.stdout.read().decode('utf-8').strip().split('\n') return {func: func for func in functions if func not in overridden} +@cache('~/.config/fish/config.fish') +def _get_aliases(overridden): + aliases = {} + proc = Popen(['fish', '-ic', 'alias'], stdout=PIPE, stderr=DEVNULL) + alias_out = proc.stdout.read().decode('utf-8').strip().split('\n') + for alias in alias_out: + name, value = alias.replace('alias ', '', 1).split(' ', 1) + if name not in overridden: + aliases[name] = value + return aliases + + class Fish(Generic): def _get_overridden_aliases(self): overridden = os.environ.get('THEFUCK_OVERRIDDEN_ALIASES', @@ -44,12 +56,17 @@ class Fish(Generic): def get_aliases(self): overridden = self._get_overridden_aliases() - return _get_aliases(overridden) + functions = _get_functions(overridden) + raw_aliases = _get_aliases(overridden) + functions.update(raw_aliases) + return functions def _expand_aliases(self, command_script): aliases = self.get_aliases() binary = command_script.split(' ')[0] - if binary in aliases: + if binary in aliases and aliases[binary] != binary: + return command_script.replace(binary, aliases[binary], 1) + elif binary in aliases: return u'fish -ic "{}"'.format(command_script.replace('"', r'\"')) else: return command_script From 4fb85b0a9280ec44ce586a7ad6869477bcf86c23 Mon Sep 17 00:00:00 2001 From: Joseph Frazier <1212jtraceur@gmail.com> Date: Wed, 3 Jan 2018 12:59:38 -0500 Subject: [PATCH 12/23] Add GitHub Issue template (#749) This prompts the user to include relevant information when reporting issues. I adapted it from the corresponding section of CONTRIBUTING.md See here for details: https://github.com/blog/2111-issue-and-pull-request-templates --- .github/ISSUE_TEMPLATE.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..aac757a --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,37 @@ + + + + +The output of `thefuck --version` (something like `The Fuck 3.1 using Python 3.5.0`): + + FILL THIS IN + +Your shell and its version (`bash`, `zsh`, *Windows PowerShell*, etc.): + + FILL THIS IN + +Your system (Debian 7, ArchLinux, Windows, etc.): + + + +How to reproduce the bug: + + FILL THIS IN + +The output of The Fuck with `THEFUCK_DEBUG=true` exported (typically execute `export THEFUCK_DEBUG=true` in your shell before The Fuck): + + FILL THIS IN + +If the bug only appears with a specific application, the output of that application and its version: + + FILL THIS IN + +Anything else you think is relevant: + + + + From 7e6d1dbc7c6cf212d8e830f412609988de188dbb Mon Sep 17 00:00:00 2001 From: Joseph Frazier <1212jtraceur@gmail.com> Date: Wed, 3 Jan 2018 13:00:20 -0500 Subject: [PATCH 13/23] Move Developing instructions from README to CONTRIBUTING (#757) * Move Developing instructions from README to CONTRIBUTING This makes them easier to find, especially for users opening issues or pull requests. See here for more details: https://help.github.com/articles/setting-guidelines-for-repository-contributors/ * fixup! Move Developing instructions from README to CONTRIBUTING --- CONTRIBUTING.md | 34 ++++++++++++++++++++++++++++++++++ README.md | 32 +------------------------------- 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a8e8d74..592a8ac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,3 +23,37 @@ It's only with enough information that we can do something to fix the problem. We gladly accept pull request on the [official repository](https://github.com/nvbn/thefuck) for new rules, new features, bug fixes, etc. + +# Developing + +Install `The Fuck` for development: + +```bash +pip install -r requirements.txt +python setup.py develop +``` + +Run code style checks: + +```bash +flake8 +``` + +Run unit tests: + +```bash +py.test +``` + +Run unit and functional tests (requires docker): + +```bash +py.test --enable-functional +``` + +For sending package to pypi: + +```bash +sudo apt-get install pandoc +./release.py +``` diff --git a/README.md b/README.md index e686d06..50bf49a 100644 --- a/README.md +++ b/README.md @@ -444,37 +444,7 @@ eval $(thefuck --alias --enable-experimental-instant-mode) ## Developing -Install `The Fuck` for development: - -```bash -pip install -r requirements.txt -python setup.py develop -``` - -Run code style checks: - -```bash -flake8 -``` - -Run unit tests: - -```bash -py.test -``` - -Run unit and functional tests (requires docker): - -```bash -py.test --enable-functional -``` - -For sending package to pypi: - -```bash -sudo apt-get install pandoc -./release.py -``` +See [CONTRIBUTING.md](CONTRIBUTING.md) ## License MIT Project License can be found [here](LICENSE.md). From a696461cd3518769864db9e65d079d3c8360e938 Mon Sep 17 00:00:00 2001 From: Joseph Frazier <1212jtraceur@gmail.com> Date: Wed, 3 Jan 2018 13:01:09 -0500 Subject: [PATCH 14/23] Add apt_upgrade rule (#761) * apt_list_upgradable: Prepend sudo to suggestion if used in command * Add apt_upgrade rule This suggests `apt upgrade` after `apt list --upgradable` if there are packages to upgrade. It pairs well with the `apt_list_upgradable` rule, which suggests `apt list --upgradable` after `apt update` if there are packages to upgrade. * Add apt_upgrade rule to README --- README.md | 1 + tests/rules/test_apt_list_upgradable.py | 3 +++ tests/rules/test_apt_upgrade.py | 36 +++++++++++++++++++++++++ thefuck/rules/apt_list_upgradable.py | 1 + thefuck/rules/apt_upgrade.py | 16 +++++++++++ 5 files changed, 57 insertions(+) create mode 100644 tests/rules/test_apt_upgrade.py create mode 100644 thefuck/rules/apt_upgrade.py diff --git a/README.md b/README.md index 50bf49a..c07f40f 100644 --- a/README.md +++ b/README.md @@ -283,6 +283,7 @@ Enabled by default only on specific platforms: * `apt_get_search` – changes trying to search using `apt-get` with searching using `apt-cache`; * `apt_invalid_operation` – fixes invalid `apt` and `apt-get` calls, like `apt-get isntall vim`; * `apt_list_upgradable` – helps you run `apt list --upgradable` after `apt update`; +* `apt_upgrade` – helps you run `apt upgrade` after `apt list --upgradable`; * `brew_cask_dependency` – installs cask dependencies; * `brew_install` – fixes formula name for `brew install`; * `brew_link` – adds `--overwrite --dry-run` if linking fails; diff --git a/tests/rules/test_apt_list_upgradable.py b/tests/rules/test_apt_list_upgradable.py index 567b855..257a92a 100644 --- a/tests/rules/test_apt_list_upgradable.py +++ b/tests/rules/test_apt_list_upgradable.py @@ -69,4 +69,7 @@ def test_not_match(command): def test_get_new_command(): new_command = get_new_command(Command('sudo apt update', match_output)) + assert new_command == 'sudo apt list --upgradable' + + new_command = get_new_command(Command('apt update', match_output)) assert new_command == 'apt list --upgradable' diff --git a/tests/rules/test_apt_upgrade.py b/tests/rules/test_apt_upgrade.py new file mode 100644 index 0000000..687d706 --- /dev/null +++ b/tests/rules/test_apt_upgrade.py @@ -0,0 +1,36 @@ +import pytest +from thefuck.rules.apt_upgrade import get_new_command, match +from thefuck.types import Command + +match_output = ''' +Listing... Done +heroku/stable 6.15.2-1 amd64 [upgradable from: 6.14.43-1] +resolvconf/zesty-updates,zesty-updates 1.79ubuntu4.1 all [upgradable from: 1.79ubuntu4] +squashfs-tools/zesty-updates 1:4.3-3ubuntu2.17.04.1 amd64 [upgradable from: 1:4.3-3ubuntu2] +unattended-upgrades/zesty-updates,zesty-updates 0.93.1ubuntu2.4 all [upgradable from: 0.93.1ubuntu2.3] +''' + +no_match_output = ''' +Listing... Done +''' + + +def test_match(): + assert match(Command('apt list --upgradable', match_output)) + assert match(Command('sudo apt list --upgradable', match_output)) + + +@pytest.mark.parametrize('command', [ + Command('apt list --upgradable', no_match_output), + Command('sudo apt list --upgradable', no_match_output) +]) +def test_not_match(command): + assert not match(command) + + +def test_get_new_command(): + new_command = get_new_command(Command('apt list --upgradable', match_output)) + assert new_command == 'apt upgrade' + + new_command = get_new_command(Command('sudo apt list --upgradable', match_output)) + assert new_command == 'sudo apt upgrade' diff --git a/thefuck/rules/apt_list_upgradable.py b/thefuck/rules/apt_list_upgradable.py index 767b201..071a748 100644 --- a/thefuck/rules/apt_list_upgradable.py +++ b/thefuck/rules/apt_list_upgradable.py @@ -11,5 +11,6 @@ def match(command): return "Run 'apt list --upgradable' to see them." in command.output +@sudo_support def get_new_command(command): return 'apt list --upgradable' diff --git a/thefuck/rules/apt_upgrade.py b/thefuck/rules/apt_upgrade.py new file mode 100644 index 0000000..6beb719 --- /dev/null +++ b/thefuck/rules/apt_upgrade.py @@ -0,0 +1,16 @@ +from thefuck.specific.apt import apt_available +from thefuck.specific.sudo import sudo_support +from thefuck.utils import for_app + +enabled_by_default = apt_available + + +@sudo_support +@for_app('apt') +def match(command): + return command.script == "apt list --upgradable" and len(command.output.strip().split('\n')) > 1 + + +@sudo_support +def get_new_command(command): + return 'apt upgrade' From b62bb90a0d769c6afa34ff18acbc29856a813d9b Mon Sep 17 00:00:00 2001 From: David Hart Date: Thu, 4 Jan 2018 16:40:01 +0000 Subject: [PATCH 15/23] git_push: Escape single quote in branch names (#760) Parameterize test output fixture. Check for 'push' in command.script_parts than anywhere in command.script. --- tests/rules/test_git_push.py | 69 +++++++++++++++++++++--------------- thefuck/rules/git_push.py | 4 +-- 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/tests/rules/test_git_push.py b/tests/rules/test_git_push.py index 785b08e..cf45f4e 100644 --- a/tests/rules/test_git_push.py +++ b/tests/rules/test_git_push.py @@ -4,38 +4,51 @@ from thefuck.types import Command @pytest.fixture -def output(): - return '''fatal: The current branch master has no upstream branch. +def output(branch_name): + if not branch_name: + return '' + return '''fatal: The current branch {} has no upstream branch. To push the current branch and set the remote as upstream, use - git push --set-upstream origin master + git push --set-upstream origin {} -''' +'''.format(branch_name, branch_name) -def test_match(output): - assert match(Command('git push', output)) - assert match(Command('git push master', output)) - assert not match(Command('git push master', '')) - assert not match(Command('ls', output)) +@pytest.mark.parametrize('script, branch_name', [ + ('git push', 'master'), + ('git push origin', 'master')]) +def test_match(output, script, branch_name): + assert match(Command(script, output)) -def test_get_new_command(output): - assert get_new_command(Command('git push', output))\ - == "git push --set-upstream origin master" - assert get_new_command(Command('git push master', output))\ - == "git push --set-upstream origin master" - assert get_new_command(Command('git push -u', output))\ - == "git push --set-upstream origin master" - assert get_new_command(Command('git push -u origin', output))\ - == "git push --set-upstream origin master" - assert get_new_command(Command('git push origin', output))\ - == "git push --set-upstream origin master" - assert get_new_command(Command('git push --set-upstream origin', output))\ - == "git push --set-upstream origin master" - assert get_new_command(Command('git push --quiet', output))\ - == "git push --set-upstream origin master --quiet" - assert get_new_command(Command('git push --quiet origin', output))\ - == "git push --set-upstream origin master --quiet" - assert get_new_command(Command('git -c test=test push --quiet origin', output))\ - == "git -c test=test push --set-upstream origin master --quiet" +@pytest.mark.parametrize('script, branch_name', [ + ('git push master', None), + ('ls', 'master')]) +def test_not_match(output, script, branch_name): + assert not match(Command(script, output)) + + +@pytest.mark.parametrize('script, branch_name, new_command', [ + ('git push', 'master', + 'git push --set-upstream origin master'), + ('git push master', 'master', + 'git push --set-upstream origin master'), + ('git push -u', 'master', + 'git push --set-upstream origin master'), + ('git push -u origin', 'master', + 'git push --set-upstream origin master'), + ('git push origin', 'master', + 'git push --set-upstream origin master'), + ('git push --set-upstream origin', 'master', + 'git push --set-upstream origin master'), + ('git push --quiet', 'master', + 'git push --set-upstream origin master --quiet'), + ('git push --quiet origin', 'master', + 'git push --set-upstream origin master --quiet'), + ('git -c test=test push --quiet origin', 'master', + 'git -c test=test push --set-upstream origin master --quiet'), + ('git push', "test's", + "git push --set-upstream origin test\\'s")]) +def test_get_new_command(output, script, branch_name, new_command): + assert get_new_command(Command(script, output)) == new_command diff --git a/thefuck/rules/git_push.py b/thefuck/rules/git_push.py index e11938b..551b25d 100644 --- a/thefuck/rules/git_push.py +++ b/thefuck/rules/git_push.py @@ -5,7 +5,7 @@ from thefuck.specific.git import git_support @git_support def match(command): - return ('push' in command.script + return ('push' in command.script_parts and 'set-upstream' in command.output) @@ -39,6 +39,6 @@ def get_new_command(command): while len(command_parts) > push_idx and command_parts[len(command_parts) - 1][0] != '-': command_parts.pop(len(command_parts) - 1) - arguments = re.findall(r'git push (.*)', command.output)[0].strip() + arguments = re.findall(r'git push (.*)', command.output)[0].replace("'", r"\'").strip() return replace_argument(" ".join(command_parts), 'push', 'push {}'.format(arguments)) From 7b10a86267dfa6e2c0fa0bebe4d4229f5e1dfd38 Mon Sep 17 00:00:00 2001 From: David Hart Date: Fri, 5 Jan 2018 21:20:03 +0000 Subject: [PATCH 16/23] Add rule for ADB unknown commands (#765) --- README.md | 1 + tests/rules/test_adb_unknown_command.py | 41 +++++++++++++++++++ thefuck/rules/adb_unknown_command.py | 54 +++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 tests/rules/test_adb_unknown_command.py create mode 100644 thefuck/rules/adb_unknown_command.py diff --git a/README.md b/README.md index c07f40f..184e20a 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,7 @@ pip3 install thefuck --upgrade The Fuck tries to match a rule for the previous command, creates a new command using the matched rule and runs it. Rules enabled by default are as follows: +* `adb_unknown_command` – fixes misspelled commands like `adb logcta`; * `ag_literal` – adds `-Q` to `ag` when suggested; * `aws_cli` – fixes misspelled commands like `aws dynamdb scan`; * `cargo` – runs `cargo build` instead of `cargo`; diff --git a/tests/rules/test_adb_unknown_command.py b/tests/rules/test_adb_unknown_command.py new file mode 100644 index 0000000..f2c3256 --- /dev/null +++ b/tests/rules/test_adb_unknown_command.py @@ -0,0 +1,41 @@ +import pytest +from thefuck.rules.adb_unknown_command import match, get_new_command +from thefuck.types import Command + + +@pytest.fixture +def output(): + return '''Android Debug Bridge version 1.0.31 + + -d - directs command to the only connected USB device + returns an error if more than one USB device is present. + -e - directs command to the only running emulator. + returns an error if more than one emulator is running. + -s - directs command to the device or emulator with the given + serial number or qualifier. Overrides ANDROID_SERIAL + environment variable. +''' + + +@pytest.mark.parametrize('script', [ + ('adb lgcat'), + ('adb puhs')]) +def test_match(output, script): + assert match(Command(script, output)) + + +@pytest.mark.parametrize('script', [ + 'git branch foo', + 'abd push']) +def test_not_match(script): + assert not match(Command(script, '')) + + +@pytest.mark.parametrize('script, new_command', [ + ('adb puhs test.bin /sdcard/test.bin', 'adb push test.bin /sdcard/test.bin'), + ('adb -s 1111 logcta', 'adb -s 1111 logcat'), + ('adb -P 666 pulll /sdcard/test.bin', 'adb -P 666 pull /sdcard/test.bin'), + ('adb -d logcatt', 'adb -d logcat'), + ('adb -e reboott', 'adb -e reboot')]) +def test_get_new_command(script, output, new_command): + assert get_new_command(Command(script, output)) == new_command diff --git a/thefuck/rules/adb_unknown_command.py b/thefuck/rules/adb_unknown_command.py new file mode 100644 index 0000000..bd33ca1 --- /dev/null +++ b/thefuck/rules/adb_unknown_command.py @@ -0,0 +1,54 @@ +from thefuck.utils import is_app, get_closest, replace_argument + + +_ADB_COMMANDS = ( + 'backup', + 'bugreport', + 'connect', + 'devices', + 'disable-verity', + 'disconnect', + 'enable-verity', + 'emu', + 'forward', + 'get-devpath', + 'get-serialno', + 'get-state', + 'install', + 'install-multiple', + 'jdwp', + 'keygen', + 'kill-server', + 'logcat', + 'pull', + 'push', + 'reboot', + 'reconnect', + 'restore', + 'reverse', + 'root', + 'run-as', + 'shell', + 'sideload', + 'start-server', + 'sync', + 'tcpip', + 'uninstall', + 'unroot', + 'usb', + 'wait-for', +) + + +def match(command): + return (is_app(command, 'adb') + and command.output.startswith('Android Debug Bridge version')) + + +def get_new_command(command): + for idx, arg in enumerate(command.script_parts[1:]): + # allowed params to ADB are a/d/e/s/H/P/L where s, H, P and L take additional args + # for example 'adb -s 111 logcat' or 'adb -e logcat' + if not arg[0] == '-' and not command.script_parts[idx] in ('-s', '-H', '-P', '-L'): + adb_cmd = get_closest(arg, _ADB_COMMANDS) + return replace_argument(command.script, arg, adb_cmd) From 797ca1c5647c565f62e21a8e29515c8b0fbe275f Mon Sep 17 00:00:00 2001 From: David Hart Date: Fri, 5 Jan 2018 21:24:43 +0000 Subject: [PATCH 17/23] Offer git commit --amend after previous git commit (#764) --- README.md | 1 + tests/rules/test_git_commit_amend.py | 25 +++++++++++++++++++++++++ thefuck/rules/git_commit_amend.py | 11 +++++++++++ 3 files changed, 37 insertions(+) create mode 100644 tests/rules/test_git_commit_amend.py create mode 100644 thefuck/rules/git_commit_amend.py diff --git a/README.md b/README.md index 184e20a..7505717 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,7 @@ using the matched rule and runs it. Rules enabled by default are as follows: * `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_commit_amend` – offers `git commit --amend` after previous commit; * `git_diff_no_index` – adds `--no-index` to previous `git diff` on untracked files; * `git_diff_staged` – adds `--staged` to previous `git diff` with unexpected output; * `git_fix_stash` – fixes `git stash` commands (misspelled subcommand and missing `save`); diff --git a/tests/rules/test_git_commit_amend.py b/tests/rules/test_git_commit_amend.py new file mode 100644 index 0000000..743ccbf --- /dev/null +++ b/tests/rules/test_git_commit_amend.py @@ -0,0 +1,25 @@ +import pytest +from thefuck.rules.git_commit_amend import match, get_new_command +from thefuck.types import Command + + +@pytest.mark.parametrize('script, output', [ + ('git commit -m "test"', 'test output'), + ('git commit', '')]) +def test_match(output, script): + assert match(Command(script, output)) + + +@pytest.mark.parametrize('script', [ + 'git branch foo', + 'git checkout feature/test_commit', + 'git push']) +def test_not_match(script): + assert not match(Command(script, '')) + + +@pytest.mark.parametrize('script', [ + ('git commit -m "test commit"'), + ('git commit')]) +def test_get_new_command(script): + assert get_new_command(Command(script, '')) == 'git commit --amend' diff --git a/thefuck/rules/git_commit_amend.py b/thefuck/rules/git_commit_amend.py new file mode 100644 index 0000000..dd0ae6d --- /dev/null +++ b/thefuck/rules/git_commit_amend.py @@ -0,0 +1,11 @@ +from thefuck.specific.git import git_support + + +@git_support +def match(command): + return ('commit' in command.script_parts) + + +@git_support +def get_new_command(command): + return 'git commit --amend' From 7c858fadb3458be829d3d43666ccb46c3ed5b8a0 Mon Sep 17 00:00:00 2001 From: David Hart Date: Fri, 5 Jan 2018 21:25:08 +0000 Subject: [PATCH 18/23] #762: handle single quotes in git_branch_exists * handle single quotes in git_branch_exists * Fix line length * Fix missing quotes from test --- tests/rules/test_git_branch_exists.py | 23 +++++++++++++++-------- thefuck/rules/git_branch_exists.py | 5 +++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/tests/rules/test_git_branch_exists.py b/tests/rules/test_git_branch_exists.py index 14c17e5..b6e0df6 100644 --- a/tests/rules/test_git_branch_exists.py +++ b/tests/rules/test_git_branch_exists.py @@ -4,8 +4,8 @@ from thefuck.types import Command @pytest.fixture -def output(branch_name): - return "fatal: A branch named '{}' already exists.".format(branch_name) +def output(src_branch_name): + return "fatal: A branch named '{}' already exists.".format(src_branch_name) @pytest.fixture @@ -17,18 +17,25 @@ def new_command(branch_name): 'git branch -D {0} && git checkout -b {0}', 'git checkout {0}']] -@pytest.mark.parametrize('script, branch_name', [ - ('git branch foo', 'foo'), ('git checkout bar', 'bar')]) +@pytest.mark.parametrize('script, src_branch_name, branch_name', [ + ('git branch foo', 'foo', 'foo'), + ('git checkout bar', 'bar', 'bar'), + ('git checkout -b "let\'s-push-this"', '"let\'s-push-this"', '"let\'s-push-this"')]) def test_match(output, script, branch_name): assert match(Command(script, output)) -@pytest.mark.parametrize('script', ['git branch foo', 'git checkout bar']) +@pytest.mark.parametrize('script', [ + 'git branch foo', + 'git checkout bar', + 'git checkout -b "let\'s-push-this"']) def test_not_match(script): assert not match(Command(script, '')) -@pytest.mark.parametrize('script, branch_name, ', [ - ('git branch foo', 'foo'), ('git checkout bar', 'bar')]) -def test_get_new_command(output, new_command, script, branch_name): +@pytest.mark.parametrize('script, src_branch_name, branch_name', [ + ('git branch foo', 'foo', 'foo'), + ('git checkout bar', 'bar', 'bar'), + ('git checkout -b "let\'s-push-this"', "let's-push-this", "let\\'s-push-this")]) +def test_get_new_command(output, new_command, script, src_branch_name, branch_name): assert get_new_command(Command(script, output)) == new_command diff --git a/thefuck/rules/git_branch_exists.py b/thefuck/rules/git_branch_exists.py index 93e11b5..4a7a822 100644 --- a/thefuck/rules/git_branch_exists.py +++ b/thefuck/rules/git_branch_exists.py @@ -7,14 +7,15 @@ from thefuck.utils import eager @git_support def match(command): return ("fatal: A branch named '" in command.output - and " already exists." in command.output) + and "' already exists." in command.output) @git_support @eager def get_new_command(command): branch_name = re.findall( - r"fatal: A branch named '([^']*)' already exists.", command.output)[0] + r"fatal: A branch named '(.+)' already exists.", command.output)[0] + branch_name = branch_name.replace("'", r"\'") new_command_templates = [['git branch -d {0}', 'git branch {0}'], ['git branch -d {0}', 'git checkout -b {0}'], ['git branch -D {0}', 'git branch {0}'], From c205683a8df8a57e2db1e9816a5a7ce3255b08fc Mon Sep 17 00:00:00 2001 From: David Hart Date: Sat, 6 Jan 2018 22:44:03 +0000 Subject: [PATCH 19/23] git_push: Handle branch names containing 'set-upstream' (#759) This should fix https://github.com/nvbn/thefuck/issues/723 (IndexError when using bitbucket) --- tests/rules/test_git_push.py | 17 +++++++++++++++++ thefuck/rules/git_push.py | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/rules/test_git_push.py b/tests/rules/test_git_push.py index cf45f4e..b6aa7c6 100644 --- a/tests/rules/test_git_push.py +++ b/tests/rules/test_git_push.py @@ -15,6 +15,19 @@ To push the current branch and set the remote as upstream, use '''.format(branch_name, branch_name) +@pytest.fixture +def output_bitbucket(): + return '''Total 0 (delta 0), reused 0 (delta 0) +remote: +remote: Create pull request for feature/set-upstream: +remote: https://bitbucket.org/set-upstream +remote: +To git@bitbucket.org:test.git + e5e7fbb..700d998 feature/set-upstream -> feature/set-upstream +Branch feature/set-upstream set up to track remote branch feature/set-upstream from origin. +''' + + @pytest.mark.parametrize('script, branch_name', [ ('git push', 'master'), ('git push origin', 'master')]) @@ -22,6 +35,10 @@ def test_match(output, script, branch_name): assert match(Command(script, output)) +def test_match_bitbucket(output_bitbucket): + assert not match(Command('git push origin', output_bitbucket)) + + @pytest.mark.parametrize('script, branch_name', [ ('git push master', None), ('ls', 'master')]) diff --git a/thefuck/rules/git_push.py b/thefuck/rules/git_push.py index 551b25d..cccee67 100644 --- a/thefuck/rules/git_push.py +++ b/thefuck/rules/git_push.py @@ -6,7 +6,7 @@ from thefuck.specific.git import git_support @git_support def match(command): return ('push' in command.script_parts - and 'set-upstream' in command.output) + and 'git push --set-upstream' in command.output) def _get_upstream_option_index(command_parts): From aa4558560162d5c6ef760645d8ca7ec8f1acdc09 Mon Sep 17 00:00:00 2001 From: "Guangyuan (Charlie) Yang" Date: Wed, 10 Jan 2018 15:31:38 -0500 Subject: [PATCH 20/23] Add installation instructions on FreeBSD (#770) misc/thefuck has recently been committed to the FreeBSD ports tree (https://svnweb.freebsd.org/ports?view=revision&revision=458123). --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 7505717..ec6549b 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,12 @@ sudo apt install python3-dev python3-pip sudo pip3 install thefuck ``` +On FreeBSD you can install `The Fuck` with: +```bash +sudo portsnap fetch update +cd /usr/ports/misc/thefuck && sudo make install clean +``` + On other systems you can install `The Fuck` with `pip`: ```bash From 027b41da593a5a6369243bb1328e0f613ac1863d Mon Sep 17 00:00:00 2001 From: Joseph Frazier <1212jtraceur@gmail.com> Date: Tue, 16 Jan 2018 20:03:56 -0500 Subject: [PATCH 21/23] Add `git_merge_unrelated` rule for `git merge --allow-unrelated-histories` (#773) From https://git-scm.com/docs/merge-options#merge-options---allow-unrelated-histories > By default, `git merge` command refuses to merge histories that do not share a common ancestor. This option can be used to override this safety when merging histories of two projects that started their lives independently. --- README.md | 1 + tests/rules/test_git_merge_unrelated.py | 25 +++++++++++++++++++++++++ thefuck/rules/git_merge_unrelated.py | 12 ++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 tests/rules/test_git_merge_unrelated.py create mode 100644 thefuck/rules/git_merge_unrelated.py diff --git a/README.md b/README.md index ec6549b..27d2d4a 100644 --- a/README.md +++ b/README.md @@ -200,6 +200,7 @@ using the matched rule and runs it. Rules enabled by default are as follows: * `git_flag_after_filename` – fixes `fatal: bad flag '...' after filename` * `git_help_aliased` – fixes `git help ` commands replacing with the aliased command; * `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`; * `git_pull` – sets upstream before executing previous `git pull`; * `git_pull_clone` – clones instead of pulling when the repo does not exist; diff --git a/tests/rules/test_git_merge_unrelated.py b/tests/rules/test_git_merge_unrelated.py new file mode 100644 index 0000000..c9a2b9e --- /dev/null +++ b/tests/rules/test_git_merge_unrelated.py @@ -0,0 +1,25 @@ +import pytest +from thefuck.rules.git_merge_unrelated import match, get_new_command +from thefuck.types import Command + + +@pytest.fixture +def output(): + return 'fatal: refusing to merge unrelated histories' + + +def test_match(output): + assert match(Command('git merge test', output)) + assert not match(Command('git merge master', '')) + assert not match(Command('ls', output)) + + +@pytest.mark.parametrize('command, new_command', [ + (Command('git merge local', output()), + 'git merge local --allow-unrelated-histories'), + (Command('git merge -m "test" local', output()), + 'git merge -m "test" local --allow-unrelated-histories'), + (Command('git merge -m "test local" local', output()), + 'git merge -m "test local" local --allow-unrelated-histories')]) +def test_get_new_command(command, new_command): + assert get_new_command(command) == new_command diff --git a/thefuck/rules/git_merge_unrelated.py b/thefuck/rules/git_merge_unrelated.py new file mode 100644 index 0000000..8e32642 --- /dev/null +++ b/thefuck/rules/git_merge_unrelated.py @@ -0,0 +1,12 @@ +from thefuck.specific.git import git_support + + +@git_support +def match(command): + return ('merge' in command.script + and 'fatal: refusing to merge unrelated histories' in command.output) + + +@git_support +def get_new_command(command): + return command.script + ' --allow-unrelated-histories' From b65e3a9aaded6c7f223597794f5b3711483aeffc Mon Sep 17 00:00:00 2001 From: Omer Katz Date: Mon, 29 Jan 2018 09:46:18 +0200 Subject: [PATCH 22/23] Added hebrew the list of keyboard layouts (#778) * Added hebrew the list of keyboard layouts. Fixes #776. * Added tests for hebrew layout. * Fix test. * Make lint happy. --- tests/rules/test_switch_lang.py | 11 ++++++++--- thefuck/rules/switch_lang.py | 3 ++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/rules/test_switch_lang.py b/tests/rules/test_switch_lang.py index 5d22f94..e2da837 100644 --- a/tests/rules/test_switch_lang.py +++ b/tests/rules/test_switch_lang.py @@ -7,7 +7,9 @@ from thefuck.types import Command @pytest.mark.parametrize('command', [ Command(u'фзе-пуе', 'command not found: фзе-пуе'), - Command(u'λσ', 'command not found: λσ')]) + Command(u'λσ', 'command not found: λσ'), + Command(u'שפא-עקא', 'command not found: שפא-עקא'), + Command(u'ךד', 'command not found: ךד')]) def test_match(command): assert switch_lang.match(command) @@ -16,13 +18,16 @@ def test_match(command): Command(u'pat-get', 'command not found: pat-get'), Command(u'ls', 'command not found: ls'), Command(u'агсл', 'command not found: агсл'), - Command(u'фзе-пуе', 'some info')]) + Command(u'фзе-пуе', 'some info'), + Command(u'שפא-עקא', 'some info')]) def test_not_match(command): assert not switch_lang.match(command) @pytest.mark.parametrize('command, new_command', [ (Command(u'фзе-пуе штыефдд мшь', ''), 'apt-get install vim'), - (Command(u'λσ -λα', ''), 'ls -la')]) + (Command(u'λσ -λα', ''), 'ls -la'), + (Command(u'שפא-עקא ןמדאשךך הןצ', ''), 'apt-get install vim'), + (Command(u'ךד -ךש', ''), 'ls -la')]) def test_get_new_command(command, new_command): assert switch_lang.get_new_command(command) == new_command diff --git a/thefuck/rules/switch_lang.py b/thefuck/rules/switch_lang.py index e67c89d..49ceec0 100644 --- a/thefuck/rules/switch_lang.py +++ b/thefuck/rules/switch_lang.py @@ -5,7 +5,8 @@ target_layout = '''qwertyuiop[]asdfghjkl;'zxcvbnm,./QWERTYUIOP{}ASDFGHJKL:"ZXCVB source_layouts = [u'''йцукенгшщзхъфывапролджэячсмитьбю.ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,''', u'''ضصثقفغعهخحجچشسیبلاتنمکگظطزرذدپو./ًٌٍَُِّْ][}{ؤئيإأآة»«:؛كٓژٰ‌ٔء><؟''', - u''';ςερτυθιοπ[]ασδφγηξκλ΄ζχψωβνμ,./:΅ΕΡΤΥΘΙΟΠ{}ΑΣΔΦΓΗΞΚΛ¨"ΖΧΨΩΒΝΜ<>?'''] + u''';ςερτυθιοπ[]ασδφγηξκλ΄ζχψωβνμ,./:΅ΕΡΤΥΘΙΟΠ{}ΑΣΔΦΓΗΞΚΛ¨"ΖΧΨΩΒΝΜ<>?''', + u'''/'קראטוןםפ][שדגכעיחלךף,זסבהנמצתץ.QWERTYUIOP{}ASDFGHJKL:"ZXCVBNM<>?'''] @memoize From dd9554539f773b510cf3321e19e7b03e5a8c16d0 Mon Sep 17 00:00:00 2001 From: JunYoung Gwak Date: Thu, 22 Feb 2018 13:14:02 -0800 Subject: [PATCH 23/23] Added a rule to delete sudo for pacaur. (#787) --- README.md | 3 ++- tests/rules/test_unsudo.py | 22 ++++++++++++++++++++++ thefuck/rules/unsudo.py | 15 +++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/rules/test_unsudo.py create mode 100644 thefuck/rules/unsudo.py diff --git a/README.md b/README.md index 27d2d4a..8b9decf 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Magnificent app which corrects your previous console command, inspired by a [@liamosaur](https://twitter.com/liamosaur/) [tweet](https://twitter.com/liamosaur/status/506975850596536320). -The Fuck is too slow? [Try experimental instant mode!](#experimental-instant-mode) +The Fuck is too slow? [Try experimental instant mode!](#experimental-instant-mode) [![gif with examples][examples-link]][examples-link] @@ -278,6 +278,7 @@ using the matched rule and runs it. Rules enabled by default are as follows: * `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. * `vagrant_up` – starts up the vagrant instance; * `whois` – fixes `whois` command; * `workon_doesnt_exists` – fixes `virtualenvwrapper` env name os suggests to create new. diff --git a/tests/rules/test_unsudo.py b/tests/rules/test_unsudo.py new file mode 100644 index 0000000..7f22599 --- /dev/null +++ b/tests/rules/test_unsudo.py @@ -0,0 +1,22 @@ +import pytest +from thefuck.rules.unsudo import match, get_new_command +from thefuck.types import Command + + +@pytest.mark.parametrize('output', [ + 'you cannot perform this operation as root']) +def test_match(output): + assert match(Command('sudo ls', output)) + + +def test_not_match(): + assert not match(Command('', '')) + assert not match(Command('sudo ls', 'Permission denied')) + assert not match(Command('ls', 'you cannot perform this operation as root')) + + +@pytest.mark.parametrize('before, after', [ + ('sudo ls', 'ls'), + ('sudo pacaur -S helloworld', 'pacaur -S helloworld')]) +def test_get_new_command(before, after): + assert get_new_command(Command(before, '')) == after diff --git a/thefuck/rules/unsudo.py b/thefuck/rules/unsudo.py new file mode 100644 index 0000000..beeac7a --- /dev/null +++ b/thefuck/rules/unsudo.py @@ -0,0 +1,15 @@ +patterns = ['you cannot perform this operation as root'] + + +def match(command): + if command.script_parts and command.script_parts[0] != 'sudo': + return False + + for pattern in patterns: + if pattern in command.output.lower(): + return True + return False + + +def get_new_command(command): + return ' '.join(command.script_parts[1:])