diff --git a/src/repository.c b/src/repository.c index ba71030b5..bcedf5d5e 100644 --- a/src/repository.c +++ b/src/repository.c @@ -634,6 +634,46 @@ Repository_merge(Repository *self, PyObject *py_oid) Py_RETURN_NONE; } +PyDoc_STRVAR(Repository_cherrypick__doc__, + "cherrypick(id)\n" + "\n" + "Cherry-pick the given oid, producing changes in the index and working directory.\n" + "\n" + "Merges the given commit into HEAD as a cherrypick, writing the results into the\n" + "working directory. Any changes are staged for commit and any conflicts\n" + "are written to the index. Callers should inspect the repository's\n" + "index after this completes, resolve any conflicts and prepare a\n" + "commit."); + +PyObject * +Repository_cherrypick(Repository *self, PyObject *py_oid) +{ + git_commit *commit; + git_oid oid; + int err; + size_t len; + git_cherrypick_options cherrypick_opts = GIT_CHERRYPICK_OPTIONS_INIT; + + len = py_oid_to_git_oid(py_oid, &oid); + if (len == 0) + return NULL; + + err = git_commit_lookup(&commit, self->repo, &oid); + if (err < 0) + return Error_set(err); + + cherrypick_opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + err = git_cherrypick(self->repo, + commit, + (const git_cherrypick_options *)&cherrypick_opts); + + git_commit_free(commit); + if (err < 0) + return Error_set(err); + + Py_RETURN_NONE; +} + PyDoc_STRVAR(Repository_walk__doc__, "walk(oid[, sort_mode]) -> iterator\n" "\n" @@ -1462,6 +1502,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, merge_base, METH_VARARGS), METHOD(Repository, merge_analysis, METH_O), METHOD(Repository, merge, METH_O), + METHOD(Repository, cherrypick, METH_O), METHOD(Repository, read, METH_O), METHOD(Repository, write, METH_VARARGS), METHOD(Repository, create_reference_direct, METH_VARARGS), diff --git a/src/repository.h b/src/repository.h index 53a760e83..b69ae22d7 100644 --- a/src/repository.h +++ b/src/repository.h @@ -70,5 +70,6 @@ PyObject* Repository_TreeBuilder(Repository *self, PyObject *args); PyObject* Repository_blame(Repository *self, PyObject *args, PyObject *kwds); PyObject* Repository_merge(Repository *self, PyObject *py_oid); +PyObject* Repository_cherrypick(Repository *self, PyObject *py_oid); #endif diff --git a/test/test_cherrypick.py b/test/test_cherrypick.py new file mode 100644 index 000000000..10798c914 --- /dev/null +++ b/test/test_cherrypick.py @@ -0,0 +1,71 @@ +# -*- coding: UTF-8 -*- +# +# Copyright 2010-2014 The pygit2 contributors +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2, +# as published by the Free Software Foundation. +# +# In addition to the permissions in the GNU General Public License, +# the authors give you unlimited permission to link the compiled +# version of this file into combinations with other programs, +# and to distribute those combinations without any restriction +# coming from the use of this file. (The General Public License +# restrictions do apply in other respects; for example, they cover +# modification of the file, and distribution when not linked into +# a combined executable.) +# +# This file is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + +"""Tests for merging and information about it.""" + +# Import from the future +from __future__ import absolute_import +from __future__ import unicode_literals + +import unittest +import os + +from pygit2 import GIT_MERGE_ANALYSIS_NONE, GIT_MERGE_ANALYSIS_NORMAL, GIT_MERGE_ANALYSIS_UP_TO_DATE +from pygit2 import GIT_MERGE_ANALYSIS_FASTFORWARD, GIT_MERGE_ANALYSIS_UNBORN +import pygit2 + +from . import utils + +class CherrypickTestBasic(utils.RepoTestCaseForMerging): + + def test_cherrypick_none(self): + self.assertRaises(TypeError, self.repo.cherrypick, None) + + def test_cherrypick_invalid_hex(self): + branch_head_hex = '12345678' + self.assertRaises(KeyError, self.repo.cherrypick, branch_head_hex) + + def test_cherrypick_already_something_in_index(self): + branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1' + branch_oid = self.repo.get(branch_head_hex).id + with open(os.path.join(self.repo.workdir, 'inindex.txt'), 'w') as f: + f.write('new content') + self.repo.index.add('inindex.txt') + self.assertRaises(pygit2.GitError, self.repo.cherrypick, branch_oid) + +class CherrypickTestWithConflicts(utils.RepoTestCaseForMerging): + + def test_cherrypick_remove_conflicts(self): + other_branch_tip = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' + self.repo.cherrypick(other_branch_tip) + idx = self.repo.index + conflicts = idx.conflicts + self.assertTrue(conflicts is not None) + conflicts['.gitignore'] + del idx.conflicts['.gitignore'] + self.assertRaises(KeyError, conflicts.__getitem__, '.gitignore') + self.assertTrue(idx.conflicts is None)