diff --git a/src/pygit2.c b/src/pygit2.c
index 3ee2ff5..3bfbb32 100644
--- a/src/pygit2.c
+++ b/src/pygit2.c
@@ -407,6 +407,27 @@ moduleinit(PyObject* m)
     PyModule_AddIntConstant(m, "LIBGIT2_VER_REVISION", LIBGIT2_VER_REVISION);
     PyModule_AddStringConstant(m, "LIBGIT2_VERSION", LIBGIT2_VERSION);
 
+    /* Different checkout strategies */
+    PyModule_AddIntConstant(m, "GIT_CHECKOUT_NONE", GIT_CHECKOUT_NONE);
+    PyModule_AddIntConstant(m, "GIT_CHECKOUT_SAFE", GIT_CHECKOUT_SAFE);
+    PyModule_AddIntConstant(m, "GIT_CHECKOUT_SAFE_CREATE",
+                            GIT_CHECKOUT_SAFE_CREATE);
+    PyModule_AddIntConstant(m, "GIT_CHECKOUT_FORCE", GIT_CHECKOUT_FORCE);
+    PyModule_AddIntConstant(m, "GIT_CHECKOUT_ALLOW_CONFLICTS",
+                            GIT_CHECKOUT_ALLOW_CONFLICTS);
+    PyModule_AddIntConstant(m, "GIT_CHECKOUT_REMOVE_UNTRACKED",
+                            GIT_CHECKOUT_REMOVE_UNTRACKED);
+    PyModule_AddIntConstant(m, "GIT_CHECKOUT_REMOVE_IGNORED",
+                            GIT_CHECKOUT_REMOVE_IGNORED);
+    PyModule_AddIntConstant(m, "GIT_CHECKOUT_UPDATE_ONLY",
+                            GIT_CHECKOUT_UPDATE_ONLY);
+    PyModule_AddIntConstant(m, "GIT_CHECKOUT_DONT_UPDATE_INDEX",
+                            GIT_CHECKOUT_DONT_UPDATE_INDEX);
+    PyModule_AddIntConstant(m, "GIT_CHECKOUT_NO_REFRESH",
+                            GIT_CHECKOUT_NO_REFRESH);
+    PyModule_AddIntConstant(m, "GIT_CHECKOUT_DISABLE_PATHSPEC_MATC",
+                            GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH);
+
     return m;
 }
 
diff --git a/src/remote.c b/src/remote.c
index 175786f..b7a6f08 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -184,7 +184,7 @@ PyDoc_STRVAR(Remote_fetch__doc__,
 PyObject *
 Remote_fetch(Remote *self, PyObject *args)
 {
-  PyObject* py_stats;
+  PyObject* py_stats = NULL;
   const git_transfer_progress *stats;
   int err;
 
diff --git a/src/repository.c b/src/repository.c
index 4bfb71e..b187ab6 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -45,6 +45,7 @@ extern PyTypeObject TreeBuilderType;
 extern PyTypeObject ConfigType;
 extern PyTypeObject DiffType;
 extern PyTypeObject RemoteType;
+extern PyTypeObject ReferenceType;
 
 git_otype
 int_to_loose_object_type(int type_id)
@@ -1066,6 +1067,53 @@ Repository_remotes__get__(Repository *self)
 }
 
 
+PyDoc_STRVAR(Repository_checkout__doc__,
+  "checkout([strategy:int, reference:Reference])\n"
+  "\n"
+  "Checks out a tree by a given reference and modifies the HEAD pointer\n"
+  "Standard checkout strategy is pygit2.GIT_CHECKOUT_SAFE_CREATE\n"
+  "If no reference is given, checkout will use HEAD instead.");
+
+PyObject *
+Repository_checkout(Repository *self, PyObject *args, PyObject *kw)
+{
+    git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+    unsigned int strategy = GIT_CHECKOUT_SAFE_CREATE;
+    Reference* ref = NULL;
+    git_object* object;
+    const git_oid* id;
+    int err, head = 0;
+
+    static char *kwlist[] = {"strategy", "reference", "head", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kw, "|IO!i", kwlist,
+                                     &strategy, &ReferenceType, &ref, &head))
+        return NULL;
+
+    if (ref != NULL) { // checkout from treeish
+        id = git_reference_target(ref->reference);
+        err = git_object_lookup(&object, self->repo, id, GIT_OBJ_COMMIT);
+        if(err == GIT_OK) {
+            opts.checkout_strategy = strategy;
+            err = git_checkout_tree(self->repo, object, &opts);
+            if (err == GIT_OK) {
+                err = git_repository_set_head(self->repo,
+                          git_reference_name(ref->reference));
+            }
+        }
+    } else { // checkout from head / index
+        opts.checkout_strategy = strategy;
+        err = (!head) ? git_checkout_index(self->repo, NULL, &opts) :
+                        git_checkout_head(self->repo, &opts);
+    }
+
+    if(err < 0)
+        return Error_set(err);
+
+    Py_RETURN_NONE;
+}
+
+
 PyMethodDef Repository_methods[] = {
     METHOD(Repository, create_blob, METH_VARARGS),
     METHOD(Repository, create_blob_fromfile, METH_VARARGS),
@@ -1083,6 +1131,7 @@ PyMethodDef Repository_methods[] = {
     METHOD(Repository, status, METH_NOARGS),
     METHOD(Repository, status_file, METH_O),
     METHOD(Repository, create_remote, METH_VARARGS),
+    METHOD(Repository, checkout, METH_VARARGS|METH_KEYWORDS),
     {NULL}
 };
 
diff --git a/test/data/testrepo.tar b/test/data/testrepo.tar
index 383d733..9c451b2 100644
Binary files a/test/data/testrepo.tar and b/test/data/testrepo.tar differ
diff --git a/test/test_repository.py b/test/test_repository.py
index e16c842..10c9885 100644
--- a/test/test_repository.py
+++ b/test/test_repository.py
@@ -184,6 +184,45 @@ class RepositoryTest_II(utils.RepoTestCase):
         expected = realpath(join(self._temp_dir, 'testrepo'))
         self.assertEqual(directory, expected)
 
+    def test_checkout_ref(self):
+        ref_i18n = self.repo.lookup_reference('refs/heads/i18n')
+
+        # checkout i18n with conflicts and default strategy should
+        # not be possible
+        self.assertRaises(pygit2.GitError,
+                          lambda: self.repo.checkout(reference=ref_i18n))
+
+        # checkout i18n with GIT_CHECKOUT_FORCE
+        self.assertTrue('new' not in self.repo.head.tree)
+        self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE, ref_i18n)
+        self.assertEqual(self.repo.head.hex, self.repo[ref_i18n.target].hex)
+        self.assertTrue('new' in self.repo.head.tree)
+        self.assertTrue('bye.txt' not in self.repo.status())
+
+    def test_checkout_index(self):
+        # some changes to working dir
+        with open(os.path.join(self.repo.workdir, 'hello.txt'), 'w') as f:
+          f.write('new content')
+
+        # checkout index
+        self.assertTrue('hello.txt' in self.repo.status())
+        self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE)
+        self.assertTrue('hello.txt' not in self.repo.status())
+
+    def test_checkout_head(self):
+        # some changes to the index
+        with open(os.path.join(self.repo.workdir, 'bye.txt'), 'w') as f:
+          f.write('new content')
+        self.repo.index.add('bye.txt')
+
+        # checkout from index should not change anything
+        self.assertTrue('bye.txt' in self.repo.status())
+        self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE)
+        self.assertTrue('bye.txt' in self.repo.status())
+
+        # checkout from head will reset index as well
+        self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE, head=True)
+        self.assertTrue('bye.txt' not in self.repo.status())
 
 class NewRepositoryTest(utils.NoRepoTestCase):
     def test_new_repo(self):
diff --git a/test/test_revwalk.py b/test/test_revwalk.py
index 4fb1fd9..1b6a083 100644
--- a/test/test_revwalk.py
+++ b/test/test_revwalk.py
@@ -44,6 +44,9 @@ log = [
     'acecd5ea2924a4b900e7e149496e1f4b57976e51']
 
 REVLOGS = [
+  ('Nico von Geyso','checkout: moving from i18n to master'),
+  ('Nico von Geyso','commit: added bye.txt and new'),
+  ('Nico von Geyso','checkout: moving from master to i18n'),
   ('J. David Ibañez', 'merge i18n: Merge made by recursive.'),
   ('J. David Ibañez', 'commit: Add .gitignore file'),
   ('J. David Ibañez', 'checkout: moving from i18n to master'),