From ae2949cfa205b5a08bcd8e039645010eda522526 Mon Sep 17 00:00:00 2001
From: lovedboy <lovedboy.tk@qq.com>
Date: Thu, 19 Nov 2015 00:15:07 +0800
Subject: [PATCH 1/9] python2.7 unicode error

---
 thefuck/shells.py | 11 +++++++++--
 thefuck/types.py  |  4 ++--
 2 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/thefuck/shells.py b/thefuck/shells.py
index 66fa1d62..6a5d1167 100644
--- a/thefuck/shells.py
+++ b/thefuck/shells.py
@@ -11,6 +11,7 @@ import io
 import os
 import shlex
 import six
+import sys
 from .utils import DEVNULL, memoize, cache
 
 
@@ -50,7 +51,10 @@ class Generic(object):
         history_file_name = self._get_history_file_name()
         if os.path.isfile(history_file_name):
             with open(history_file_name, 'a') as history:
-                history.write(self._get_history_line(command_script))
+                if sys.version_info >= (3, 0):
+                    history.write(self._get_history_line(command_script))
+                else:
+                    history.write(self._get_history_line(command_script).encode("utf-8"))
 
     def _script_from_history(self, line):
         """Returns prepared history line.
@@ -80,7 +84,10 @@ class Generic(object):
 
     def split_command(self, command):
         """Split the command using shell-like syntax."""
-        return shlex.split(command)
+        if sys.version_info >= (3, 0):
+            return shlex.split(command)
+        else:
+            return shlex.split(command.encode("utf-8"))
 
     def quote(self, s):
         """Return a shell-escaped version of the string s."""
diff --git a/thefuck/types.py b/thefuck/types.py
index a2cd9623..3bc39030 100644
--- a/thefuck/types.py
+++ b/thefuck/types.py
@@ -31,7 +31,7 @@ class Command(object):
             try:
                 self._script_parts = shells.split_command(self.script)
             except Exception:
-                logs.debug("Can't split command script {} because:\n {}".format(
+                logs.debug(u"Can't split command script {} because:\n {}".format(
                     self, sys.exc_info()))
                 self._script_parts = None
         return self._script_parts
@@ -44,7 +44,7 @@ class Command(object):
             return False
 
     def __repr__(self):
-        return 'Command(script={}, stdout={}, stderr={})'.format(
+        return u'Command(script={}, stdout={}, stderr={})'.format(
             self.script, self.stdout, self.stderr)
 
     def update(self, **kwargs):

From 465f6191b0e553eb6f8a96c060e69521c1b12eea Mon Sep 17 00:00:00 2001
From: Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
Date: Sun, 22 Nov 2015 01:19:24 -0200
Subject: [PATCH 2/9] #N/A Cleanup and adjust syntax

---
 tests/rules/test_grep_recursive.py | 3 +--
 thefuck/rules/fix_file.py          | 2 +-
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/tests/rules/test_grep_recursive.py b/tests/rules/test_grep_recursive.py
index 6c459496..4e82688f 100644
--- a/tests/rules/test_grep_recursive.py
+++ b/tests/rules/test_grep_recursive.py
@@ -8,5 +8,4 @@ def test_match():
 
 
 def test_get_new_command():
-    assert get_new_command(
-        Command('grep blah .')) == 'grep -r blah .'
+    assert get_new_command(Command('grep blah .')) == 'grep -r blah .'
diff --git a/thefuck/rules/fix_file.py b/thefuck/rules/fix_file.py
index 13a46b3b..753426e2 100644
--- a/thefuck/rules/fix_file.py
+++ b/thefuck/rules/fix_file.py
@@ -38,7 +38,7 @@ patterns = (
 def _make_pattern(pattern):
     pattern = pattern.replace('{file}', '(?P<file>[^:\n]+)') \
                      .replace('{line}', '(?P<line>[0-9]+)') \
-                     .replace('{col}',  '(?P<col>[0-9]+)')
+                     .replace('{col}', '(?P<col>[0-9]+)')
     return re.compile(pattern, re.MULTILINE)
 patterns = [_make_pattern(p).search for p in patterns]
 

From 4a7b335d7ca8c65955f35b919f01e43a312f50f5 Mon Sep 17 00:00:00 2001
From: Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
Date: Sun, 22 Nov 2015 01:57:20 -0200
Subject: [PATCH 3/9] #N/A Add ability to get Fish Shell history

---
 tests/test_shells.py | 5 +++++
 thefuck/shells.py    | 6 ++++++
 2 files changed, 11 insertions(+)

diff --git a/tests/test_shells.py b/tests/test_shells.py
index cd98a909..557b7907 100644
--- a/tests/test_shells.py
+++ b/tests/test_shells.py
@@ -180,6 +180,11 @@ class TestFish(object):
         assert 'thefuck' in shell.app_alias('fuck')
         assert 'TF_ALIAS' in shell.app_alias('fuck')
 
+    def test_get_history(self, history_lines, shell):
+        history_lines(['- cmd: ls', '  when: 1432613911',
+                       '- cmd: rm', '  when: 1432613916'])
+        assert list(shell.get_history()) == ['ls', 'rm']
+
 
 @pytest.mark.usefixtures('isfile')
 class TestZsh(object):
diff --git a/thefuck/shells.py b/thefuck/shells.py
index 6a5d1167..d67414de 100644
--- a/thefuck/shells.py
+++ b/thefuck/shells.py
@@ -191,6 +191,12 @@ class Fish(Generic):
     def _get_history_line(self, command_script):
         return u'- cmd: {}\n   when: {}\n'.format(command_script, int(time()))
 
+    def _script_from_history(self, line):
+        if '- cmd: ' in line:
+            return line.split('- cmd: ', 1)[1]
+        else:
+            return ''
+
     def and_(self, *commands):
         return u'; and '.join(commands)
 

From ad3db4ac67f75694fec8ccb6dd9f293aaf2f72d7 Mon Sep 17 00:00:00 2001
From: Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
Date: Sun, 22 Nov 2015 11:38:59 -0200
Subject: [PATCH 4/9] #N/A Fix F812 list comprehension redefines `cmd`

---
 thefuck/rules/tmux.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/thefuck/rules/tmux.py b/thefuck/rules/tmux.py
index 0aeaee28..73a90756 100644
--- a/thefuck/rules/tmux.py
+++ b/thefuck/rules/tmux.py
@@ -13,6 +13,6 @@ def get_new_command(command):
                    command.stderr)
 
     old_cmd = cmd.group(1)
-    suggestions = [cmd.strip() for cmd in cmd.group(2).split(',')]
+    suggestions = [c.strip() for c in cmd.group(2).split(',')]
 
     return replace_command(command, old_cmd, suggestions)

From fc053642333eff13f07a77d328b14a5270383369 Mon Sep 17 00:00:00 2001
From: Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
Date: Wed, 4 Nov 2015 23:08:13 -0200
Subject: [PATCH 5/9] #398 & #408: Support non-ascii IO in Python 2

---
 tests/rules/conftest.py            |  2 +-
 tests/rules/test_dirty_unzip.py    | 76 +++++++++++++++++++-----------
 tests/rules/test_fix_file.py       | 20 +++++++-
 tests/rules/test_grep_recursive.py |  4 ++
 tests/test_corrector.py            |  4 ++
 tests/test_shells.py               | 34 +++++++++----
 tests/test_types.py                |  8 ++++
 thefuck/corrector.py               |  2 +-
 thefuck/rules/cd_correction.py     |  8 +++-
 thefuck/rules/dirty_unzip.py       |  4 +-
 thefuck/rules/fix_file.py          |  2 +-
 thefuck/rules/grep_recursive.py    |  2 +-
 thefuck/shells.py                  | 15 +++---
 thefuck/types.py                   |  3 +-
 14 files changed, 129 insertions(+), 55 deletions(-)

diff --git a/tests/rules/conftest.py b/tests/rules/conftest.py
index 94152a6c..99a5c0d3 100644
--- a/tests/rules/conftest.py
+++ b/tests/rules/conftest.py
@@ -3,4 +3,4 @@ import pytest
 
 @pytest.fixture(autouse=True)
 def generic_shell(monkeypatch):
-    monkeypatch.setattr('thefuck.shells.and_', lambda *x: ' && '.join(x))
+    monkeypatch.setattr('thefuck.shells.and_', lambda *x: u' && '.join(x))
diff --git a/tests/rules/test_dirty_unzip.py b/tests/rules/test_dirty_unzip.py
index 9c0b4e51..44de835f 100644
--- a/tests/rules/test_dirty_unzip.py
+++ b/tests/rules/test_dirty_unzip.py
@@ -1,50 +1,72 @@
+# -*- coding: utf-8 -*-
+
 import os
 import pytest
 import zipfile
 from thefuck.rules.dirty_unzip import match, get_new_command, side_effect
 from tests.utils import Command
+from unicodedata import normalize
 
 
 @pytest.fixture
 def zip_error(tmpdir):
-    path = os.path.join(str(tmpdir), 'foo.zip')
+    def zip_error_inner(filename):
+        path = os.path.join(str(tmpdir), filename)
 
-    def reset(path):
-        with zipfile.ZipFile(path, 'w') as archive:
-            archive.writestr('a', '1')
-            archive.writestr('b', '2')
-            archive.writestr('c', '3')
+        def reset(path):
+            with zipfile.ZipFile(path, 'w') as archive:
+                archive.writestr('a', '1')
+                archive.writestr('b', '2')
+                archive.writestr('c', '3')
 
-            archive.writestr('d/e', '4')
+                archive.writestr('d/e', '4')
 
-            archive.extractall()
+                archive.extractall()
 
-    os.chdir(str(tmpdir))
-    reset(path)
+        os.chdir(str(tmpdir))
+        reset(path)
 
-    assert set(os.listdir('.')) == {'foo.zip', 'a', 'b', 'c', 'd'}
-    assert set(os.listdir('./d')) == {'e'}
+        dir_list = os.listdir(u'.')
+        if filename not in dir_list:
+            filename = normalize('NFD', filename)
+
+        assert set(dir_list) == {filename, 'a', 'b', 'c', 'd'}
+        assert set(os.listdir('./d')) == {'e'}
+    return zip_error_inner
 
 
-@pytest.mark.parametrize('script', [
-    'unzip foo',
-    'unzip foo.zip'])
-def test_match(zip_error, script):
+@pytest.mark.parametrize('script,filename', [
+    (u'unzip café', u'café.zip'),
+    (u'unzip café.zip', u'café.zip'),
+    (u'unzip foo', u'foo.zip'),
+    (u'unzip foo.zip', u'foo.zip')])
+def test_match(zip_error, script, filename):
+    zip_error(filename)
     assert match(Command(script=script))
 
 
-@pytest.mark.parametrize('script', [
-    'unzip foo',
-    'unzip foo.zip'])
-def test_side_effect(zip_error, script):
+@pytest.mark.parametrize('script,filename', [
+    (u'unzip café', u'café.zip'),
+    (u'unzip café.zip', u'café.zip'),
+    (u'unzip foo', u'foo.zip'),
+    (u'unzip foo.zip', u'foo.zip')])
+def test_side_effect(zip_error, script, filename):
+    zip_error(filename)
     side_effect(Command(script=script), None)
-    assert set(os.listdir('.')) == {'foo.zip', 'd'}
+
+    dir_list = os.listdir(u'.')
+    if filename not in set(dir_list):
+        filename = normalize('NFD', filename)
+
+    assert set(dir_list) == {filename, 'd'}
 
 
-@pytest.mark.parametrize('script,fixed', [
-    ('unzip foo', 'unzip foo -d foo'),
-    (R"unzip foo\ bar.zip", R"unzip foo\ bar.zip -d 'foo bar'"),
-    (R"unzip 'foo bar.zip'", R"unzip 'foo bar.zip' -d 'foo bar'"),
-    ('unzip foo.zip', 'unzip foo.zip -d foo')])
-def test_get_new_command(zip_error, script, fixed):
+@pytest.mark.parametrize('script,fixed,filename', [
+    (u'unzip café', u"unzip café -d 'café'", u'café.zip'),
+    (u'unzip foo', u'unzip foo -d foo', u'foo.zip'),
+    (u"unzip foo\\ bar.zip", u"unzip foo\\ bar.zip -d 'foo bar'", u'foo.zip'),
+    (u"unzip 'foo bar.zip'", u"unzip 'foo bar.zip' -d 'foo bar'", u'foo.zip'),
+    (u'unzip foo.zip', u'unzip foo.zip -d foo', u'foo.zip')])
+def test_get_new_command(zip_error, script, fixed, filename):
+    zip_error(filename)
     assert get_new_command(Command(script=script)) == fixed
diff --git a/tests/rules/test_fix_file.py b/tests/rules/test_fix_file.py
index c285aa2d..30f5fa1d 100644
--- a/tests/rules/test_fix_file.py
+++ b/tests/rules/test_fix_file.py
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+
 import pytest
 import os
 from thefuck.rules.fix_file import match, get_new_command
@@ -87,6 +89,20 @@ Traceback (most recent call last):
 TypeError: first argument must be string or compiled pattern
 """),
 
+(u'python café.py', u'café.py', 8, None, '',
+u"""
+Traceback (most recent call last):
+  File "café.py", line 8, in <module>
+    match("foo")
+  File "café.py", line 5, in match
+    m = re.search(None, command)
+  File "/usr/lib/python3.4/re.py", line 170, in search
+    return _compile(pattern, flags).search(string)
+  File "/usr/lib/python3.4/re.py", line 293, in _compile
+    raise TypeError("first argument must be string or compiled pattern")
+TypeError: first argument must be string or compiled pattern
+"""),
+
 ('ruby a.rb', 'a.rb', 3, None, '',
 """
 a.rb:3: syntax error, unexpected keyword_end
@@ -227,7 +243,7 @@ def test_get_new_command_with_settings(mocker, monkeypatch, test, settings):
 
     if test[3]:
         assert (get_new_command(cmd) ==
-            'dummy_editor {} +{}:{} && {}'.format(test[1], test[2], test[3], test[0]))
+            u'dummy_editor {} +{}:{} && {}'.format(test[1], test[2], test[3], test[0]))
     else:
         assert (get_new_command(cmd) ==
-            'dummy_editor {} +{} && {}'.format(test[1], test[2], test[0]))
+            u'dummy_editor {} +{} && {}'.format(test[1], test[2], test[0]))
diff --git a/tests/rules/test_grep_recursive.py b/tests/rules/test_grep_recursive.py
index 4e82688f..1bf248f1 100644
--- a/tests/rules/test_grep_recursive.py
+++ b/tests/rules/test_grep_recursive.py
@@ -1,11 +1,15 @@
+# -*- coding: utf-8 -*-
+
 from thefuck.rules.grep_recursive import match, get_new_command
 from tests.utils import Command
 
 
 def test_match():
     assert match(Command('grep blah .', stderr='grep: .: Is a directory'))
+    assert match(Command(u'grep café .', stderr='grep: .: Is a directory'))
     assert not match(Command())
 
 
 def test_get_new_command():
     assert get_new_command(Command('grep blah .')) == 'grep -r blah .'
+    assert get_new_command(Command(u'grep café .')) == u'grep -r café .'
diff --git a/tests/test_corrector.py b/tests/test_corrector.py
index 399d0878..529025b5 100644
--- a/tests/test_corrector.py
+++ b/tests/test_corrector.py
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+
 import pytest
 from pathlib import PosixPath
 from thefuck import corrector, conf
@@ -53,7 +55,9 @@ def test_organize_commands():
     """Ensures that the function removes duplicates and sorts commands."""
     commands = [CorrectedCommand('ls'), CorrectedCommand('ls -la', priority=9000),
                 CorrectedCommand('ls -lh', priority=100),
+                CorrectedCommand(u'echo café', priority=200),
                 CorrectedCommand('ls -lh', priority=9999)]
     assert list(organize_commands(iter(commands))) \
         == [CorrectedCommand('ls'), CorrectedCommand('ls -lh', priority=100),
+            CorrectedCommand(u'echo café', priority=200),
             CorrectedCommand('ls -la', priority=9000)]
diff --git a/tests/test_shells.py b/tests/test_shells.py
index 557b7907..39875fea 100644
--- a/tests/test_shells.py
+++ b/tests/test_shells.py
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+
 import pytest
 from thefuck import shells
 
@@ -35,6 +37,7 @@ class TestGeneric(object):
 
     def test_put_to_history(self, builtins_open, shell):
         assert shell.put_to_history('ls') is None
+        assert shell.put_to_history(u'echo café') is None
         assert builtins_open.call_count == 0
 
     def test_and_(self, shell):
@@ -55,6 +58,10 @@ class TestGeneric(object):
         # so just ignore them:
         assert list(shell.get_history()) == []
 
+    def test_split_command(self, shell):
+        assert shell.split_command('ls') == ['ls']
+        assert shell.split_command(u'echo café') == [u'echo', u'café']
+
 
 @pytest.mark.usefixtures('isfile')
 class TestBash(object):
@@ -83,10 +90,13 @@ class TestBash(object):
     def test_to_shell(self, shell):
         assert shell.to_shell('pwd') == 'pwd'
 
-    def test_put_to_history(self, builtins_open, shell):
-        shell.put_to_history('ls')
+    @pytest.mark.parametrize('entry, entry_utf8', [
+        ('ls', 'ls\n'),
+        (u'echo café', 'echo café\n')])
+    def test_put_to_history(self, entry, entry_utf8, builtins_open, shell):
+        shell.put_to_history(entry)
         builtins_open.return_value.__enter__.return_value. \
-            write.assert_called_once_with('ls\n')
+            write.assert_called_once_with(entry_utf8)
 
     def test_and_(self, shell):
         assert shell.and_('ls', 'cd') == 'ls && cd'
@@ -152,12 +162,15 @@ class TestFish(object):
     def test_to_shell(self, shell):
         assert shell.to_shell('pwd') == 'pwd'
 
-    def test_put_to_history(self, builtins_open, mocker, shell):
+    @pytest.mark.parametrize('entry, entry_utf8', [
+        ('ls', '- cmd: ls\n   when: 1430707243\n'),
+        (u'echo café', '- cmd: echo café\n   when: 1430707243\n')])
+    def test_put_to_history(self, entry, entry_utf8, builtins_open, mocker, shell):
         mocker.patch('thefuck.shells.time',
                      return_value=1430707243.3517463)
-        shell.put_to_history('ls')
+        shell.put_to_history(entry)
         builtins_open.return_value.__enter__.return_value. \
-            write.assert_called_once_with('- cmd: ls\n   when: 1430707243\n')
+            write.assert_called_once_with(entry_utf8)
 
     def test_and_(self, shell):
         assert shell.and_('foo', 'bar') == 'foo; and bar'
@@ -212,12 +225,15 @@ class TestZsh(object):
     def test_to_shell(self, shell):
         assert shell.to_shell('pwd') == 'pwd'
 
-    def test_put_to_history(self, builtins_open, mocker, shell):
+    @pytest.mark.parametrize('entry, entry_utf8', [
+        ('ls', ': 1430707243:0;ls\n'),
+        (u'echo café', ': 1430707243:0;echo café\n')])
+    def test_put_to_history(self, entry, entry_utf8, builtins_open, mocker, shell):
         mocker.patch('thefuck.shells.time',
                      return_value=1430707243.3517463)
-        shell.put_to_history('ls')
+        shell.put_to_history(entry)
         builtins_open.return_value.__enter__.return_value. \
-            write.assert_called_once_with(': 1430707243:0;ls\n')
+            write.assert_called_once_with(entry_utf8)
 
     def test_and_(self, shell):
         assert shell.and_('ls', 'cd') == 'ls && cd'
diff --git a/tests/test_types.py b/tests/test_types.py
index 54452a0a..c5f16445 100644
--- a/tests/test_types.py
+++ b/tests/test_types.py
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+
 from subprocess import PIPE
 from mock import Mock
 from pathlib import Path
@@ -19,6 +21,12 @@ class TestCorrectedCommand(object):
         assert {CorrectedCommand('ls', None, 100),
                 CorrectedCommand('ls', None, 200)} == {CorrectedCommand('ls')}
 
+    def test_representable(self):
+        assert '{}'.format(CorrectedCommand('ls', None, 100)) == \
+               'CorrectedCommand(script=ls, side_effect=None, priority=100)'
+        assert u'{}'.format(CorrectedCommand(u'echo café', None, 100)) == \
+               u'CorrectedCommand(script=echo café, side_effect=None, priority=100)'
+
 
 class TestRule(object):
     def test_from_path(self, mocker):
diff --git a/thefuck/corrector.py b/thefuck/corrector.py
index 2b16cca6..f08c7a74 100644
--- a/thefuck/corrector.py
+++ b/thefuck/corrector.py
@@ -55,7 +55,7 @@ def organize_commands(corrected_commands):
         key=lambda corrected_command: corrected_command.priority)
 
     logs.debug('Corrected commands: '.format(
-        ', '.join(str(cmd) for cmd in [first_command] + sorted_commands)))
+        ', '.join(u'{}'.format(cmd) for cmd in [first_command] + sorted_commands)))
 
     for command in sorted_commands:
         yield command
diff --git a/thefuck/rules/cd_correction.py b/thefuck/rules/cd_correction.py
index 5adab757..231ca799 100644
--- a/thefuck/rules/cd_correction.py
+++ b/thefuck/rules/cd_correction.py
@@ -1,6 +1,7 @@
 """Attempts to spellcheck and correct failed cd commands"""
 
 import os
+import six
 from difflib import get_close_matches
 from thefuck.specific.sudo import sudo_support
 from thefuck.rules import cd_mkdir
@@ -36,7 +37,10 @@ def get_new_command(command):
     dest = command.script_parts[1].split(os.sep)
     if dest[-1] == '':
         dest = dest[:-1]
-    cwd = os.getcwd()
+    if six.PY2:
+        cwd = os.getcwdu()
+    else:
+        cwd = os.getcwd()
     for directory in dest:
         if directory == ".":
             continue
@@ -48,7 +52,7 @@ def get_new_command(command):
             cwd = os.path.join(cwd, best_matches[0])
         else:
             return cd_mkdir.get_new_command(command)
-    return 'cd "{0}"'.format(cwd)
+    return u'cd "{0}"'.format(cwd)
 
 
 enabled_by_default = True
diff --git a/thefuck/rules/dirty_unzip.py b/thefuck/rules/dirty_unzip.py
index f15d6e99..3e45ea37 100644
--- a/thefuck/rules/dirty_unzip.py
+++ b/thefuck/rules/dirty_unzip.py
@@ -19,7 +19,7 @@ def _zip_file(command):
             if c.endswith('.zip'):
                 return c
             else:
-                return '{}.zip'.format(c)
+                return u'{}.zip'.format(c)
 
 
 @for_app('unzip')
@@ -29,7 +29,7 @@ def match(command):
 
 
 def get_new_command(command):
-    return '{} -d {}'.format(command.script, quote(_zip_file(command)[:-4]))
+    return u'{} -d {}'.format(command.script, quote(_zip_file(command)[:-4]))
 
 
 def side_effect(old_cmd, command):
diff --git a/thefuck/rules/fix_file.py b/thefuck/rules/fix_file.py
index 753426e2..fcae4a67 100644
--- a/thefuck/rules/fix_file.py
+++ b/thefuck/rules/fix_file.py
@@ -58,7 +58,7 @@ def match(command):
     return _search(command.stderr) or _search(command.stdout)
 
 
-@default_settings({'fixlinecmd': '{editor} {file} +{line}',
+@default_settings({'fixlinecmd': u'{editor} {file} +{line}',
                    'fixcolcmd': None})
 def get_new_command(command):
     m = _search(command.stderr) or _search(command.stdout)
diff --git a/thefuck/rules/grep_recursive.py b/thefuck/rules/grep_recursive.py
index af92300d..3f793a18 100644
--- a/thefuck/rules/grep_recursive.py
+++ b/thefuck/rules/grep_recursive.py
@@ -7,4 +7,4 @@ def match(command):
 
 
 def get_new_command(command):
-    return 'grep -r {}'.format(command.script[5:])
+    return u'grep -r {}'.format(command.script[5:])
diff --git a/thefuck/shells.py b/thefuck/shells.py
index d67414de..10f56a85 100644
--- a/thefuck/shells.py
+++ b/thefuck/shells.py
@@ -11,7 +11,6 @@ import io
 import os
 import shlex
 import six
-import sys
 from .utils import DEVNULL, memoize, cache
 
 
@@ -51,10 +50,11 @@ class Generic(object):
         history_file_name = self._get_history_file_name()
         if os.path.isfile(history_file_name):
             with open(history_file_name, 'a') as history:
-                if sys.version_info >= (3, 0):
-                    history.write(self._get_history_line(command_script))
+                entry = self._get_history_line(command_script)
+                if six.PY2:
+                    history.write(entry.encode('utf-8'))
                 else:
-                    history.write(self._get_history_line(command_script).encode("utf-8"))
+                    history.write(entry)
 
     def _script_from_history(self, line):
         """Returns prepared history line.
@@ -84,10 +84,9 @@ class Generic(object):
 
     def split_command(self, command):
         """Split the command using shell-like syntax."""
-        if sys.version_info >= (3, 0):
-            return shlex.split(command)
-        else:
-            return shlex.split(command.encode("utf-8"))
+        if six.PY2:
+            return [s.decode('utf8') for s in shlex.split(command.encode('utf8'))]
+        return shlex.split(command)
 
     def quote(self, s):
         """Return a shell-escaped version of the string s."""
diff --git a/thefuck/types.py b/thefuck/types.py
index 3bc39030..45542490 100644
--- a/thefuck/types.py
+++ b/thefuck/types.py
@@ -267,7 +267,7 @@ class CorrectedCommand(object):
         return (self.script, self.side_effect).__hash__()
 
     def __repr__(self):
-        return 'CorrectedCommand(script={}, side_effect={}, priority={})'.format(
+        return u'CorrectedCommand(script={}, side_effect={}, priority={})'.format(
             self.script, self.side_effect, self.priority)
 
     def run(self, old_cmd):
@@ -279,4 +279,5 @@ class CorrectedCommand(object):
         if self.side_effect:
             compatibility_call(self.side_effect, old_cmd, self.script)
         shells.put_to_history(self.script)
+        # This depends on correct setting of PYTHONIOENCODING by the alias:
         print(self.script)

From b0adc7f2ca99379be5df39eccec11b0f99c97b5d Mon Sep 17 00:00:00 2001
From: Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
Date: Mon, 23 Nov 2015 12:44:50 -0200
Subject: [PATCH 6/9] #N/A Indent Fish alias with two spaces (default)

---
 thefuck/shells.py | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/thefuck/shells.py b/thefuck/shells.py
index 10f56a85..973641d7 100644
--- a/thefuck/shells.py
+++ b/thefuck/shells.py
@@ -151,18 +151,18 @@ class Fish(Generic):
 
     def app_alias(self, fuck):
         return ('function {0} -d "Correct your previous console command"\n'
-                '    set -l exit_code $status\n'
-                '    set -x TF_ALIAS {0}\n'
-                '    set -l fucked_up_command $history[1]\n'
-                '    thefuck $fucked_up_command | read -l unfucked_command\n'
-                '    if [ "$unfucked_command" != "" ]\n'
-                '        eval $unfucked_command\n'
-                '        if test $exit_code -ne 0\n'
-                '            history --delete $fucked_up_command\n'
-                '            history --merge ^ /dev/null\n'
-                '            return 0\n'
-                '        end\n'
+                '  set -l exit_code $status\n'
+                '  set -x TF_ALIAS {0}\n'
+                '  set -l fucked_up_command $history[1]\n'
+                '  thefuck $fucked_up_command | read -l unfucked_command\n'
+                '  if [ "$unfucked_command" != "" ]\n'
+                '    eval $unfucked_command\n'
+                '    if test $exit_code -ne 0\n'
+                '      history --delete $fucked_up_command\n'
+                '      history --merge ^ /dev/null\n'
+                '      return 0\n'
                 '    end\n'
+                '  end\n'
                 'end').format(fuck)
 
     @memoize

From 959680d24d9cfe6a37373ba040ec73033cb554d1 Mon Sep 17 00:00:00 2001
From: Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
Date: Mon, 23 Nov 2015 12:44:50 -0200
Subject: [PATCH 7/9] #N/A Set TF_ALIAS as an environment variable

For more info, check:

http://fishshell.com/docs/current/faq.html#faq-single-env
---
 thefuck/shells.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/thefuck/shells.py b/thefuck/shells.py
index 973641d7..7ac79c81 100644
--- a/thefuck/shells.py
+++ b/thefuck/shells.py
@@ -152,9 +152,9 @@ class Fish(Generic):
     def app_alias(self, fuck):
         return ('function {0} -d "Correct your previous console command"\n'
                 '  set -l exit_code $status\n'
-                '  set -x TF_ALIAS {0}\n'
                 '  set -l fucked_up_command $history[1]\n'
-                '  thefuck $fucked_up_command | read -l unfucked_command\n'
+                '  env TF_ALIAS={0}'
+                '    thefuck $fucked_up_command | read -l unfucked_command\n'
                 '  if [ "$unfucked_command" != "" ]\n'
                 '    eval $unfucked_command\n'
                 '    if test $exit_code -ne 0\n'

From 6c3d67763a7d7fa4a2ba95f609643e55deee95b6 Mon Sep 17 00:00:00 2001
From: Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
Date: Mon, 23 Nov 2015 12:44:50 -0200
Subject: [PATCH 8/9] #398: Add `PYTHONIOENCODING=utf-8` to Fish Shell alias

---
 thefuck/shells.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/thefuck/shells.py b/thefuck/shells.py
index 7ac79c81..401a5a9b 100644
--- a/thefuck/shells.py
+++ b/thefuck/shells.py
@@ -153,7 +153,7 @@ class Fish(Generic):
         return ('function {0} -d "Correct your previous console command"\n'
                 '  set -l exit_code $status\n'
                 '  set -l fucked_up_command $history[1]\n'
-                '  env TF_ALIAS={0}'
+                '  env TF_ALIAS={0} PYTHONIOENCODING=utf-8'
                 '    thefuck $fucked_up_command | read -l unfucked_command\n'
                 '  if [ "$unfucked_command" != "" ]\n'
                 '    eval $unfucked_command\n'

From 3b4b87d8ed9dbe5b7db5c0a955df81c5cc159206 Mon Sep 17 00:00:00 2001
From: Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
Date: Wed, 25 Nov 2015 02:32:05 -0200
Subject: [PATCH 9/9] #398: Test `PYTHONIOENCODING=utf-8` in shell aliases

---
 tests/test_shells.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/tests/test_shells.py b/tests/test_shells.py
index 39875fea..54bde72a 100644
--- a/tests/test_shells.py
+++ b/tests/test_shells.py
@@ -51,6 +51,7 @@ class TestGeneric(object):
         assert 'alias FUCK' in shell.app_alias('FUCK')
         assert 'thefuck' in shell.app_alias('fuck')
         assert 'TF_ALIAS' in shell.app_alias('fuck')
+        assert 'PYTHONIOENCODING=utf-8' in shell.app_alias('fuck')
 
     def test_get_history(self, history_lines, shell):
         history_lines(['ls', 'rm'])
@@ -112,6 +113,7 @@ class TestBash(object):
         assert 'alias FUCK' in shell.app_alias('FUCK')
         assert 'thefuck' in shell.app_alias('fuck')
         assert 'TF_ALIAS' in shell.app_alias('fuck')
+        assert 'PYTHONIOENCODING=utf-8' in shell.app_alias('fuck')
 
     def test_get_history(self, history_lines, shell):
         history_lines(['ls', 'rm'])
@@ -192,6 +194,7 @@ class TestFish(object):
         assert 'function FUCK' in shell.app_alias('FUCK')
         assert 'thefuck' in shell.app_alias('fuck')
         assert 'TF_ALIAS' in shell.app_alias('fuck')
+        assert 'PYTHONIOENCODING=utf-8' in shell.app_alias('fuck')
 
     def test_get_history(self, history_lines, shell):
         history_lines(['- cmd: ls', '  when: 1432613911',
@@ -250,6 +253,7 @@ class TestZsh(object):
         assert 'alias FUCK' in shell.app_alias('FUCK')
         assert 'thefuck' in shell.app_alias('fuck')
         assert 'TF_ALIAS' in shell.app_alias('fuck')
+        assert 'PYTHONIOENCODING=utf-8' in shell.app_alias('fuck')
 
     def test_get_history(self, history_lines, shell):
         history_lines([': 1432613911:0;ls', ': 1432613916:0;rm'])