implementing merge with default options

This commit is contained in:
Victor Garcia
2013-12-04 18:17:10 +01:00
parent 82ac7c83af
commit 1f5ec810ad
8 changed files with 333 additions and 4 deletions

161
src/mergeresult.c Normal file
View File

@@ -0,0 +1,161 @@
/*
* Copyright 2010-2013 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.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "utils.h"
#include "types.h"
#include "oid.h"
#include "repository.h"
#include "mergeresult.h"
extern PyTypeObject MergeResultType;
extern PyTypeObject IndexType;
PyObject *
git_merge_result_to_python(git_merge_result *merge_result, Repository *repo)
{
git_oid fastforward_oid;
MergeResult *py_merge_result;
py_merge_result = PyObject_New(MergeResult, &MergeResultType);
py_merge_result->is_uptodate = git_merge_result_is_uptodate(merge_result) == GIT_CVAR_TRUE;
if (git_merge_result_is_fastforward(merge_result) == GIT_CVAR_TRUE)
{
py_merge_result->is_fastforward = GIT_CVAR_TRUE;
git_merge_result_fastforward_oid(&fastforward_oid, merge_result);
py_merge_result->fastforward_oid = git_oid_to_python((const git_oid *)&fastforward_oid);
}
else
{
py_merge_result->is_fastforward = GIT_CVAR_FALSE;
py_merge_result->fastforward_oid = NULL;
}
py_merge_result->status = Repository_status(repo);
return (PyObject*) py_merge_result;
}
PyDoc_STRVAR(MergeResult_is_uptodate__doc__, "Is up to date");
PyObject *
MergeResult_is_uptodate__get__(MergeResult *self)
{
if (self->is_uptodate)
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
}
PyDoc_STRVAR(MergeResult_is_fastforward__doc__, "Is fastforward");
PyObject *
MergeResult_is_fastforward__get__(MergeResult *self)
{
if (self->is_fastforward)
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
}
PyDoc_STRVAR(MergeResult_fastforward_oid__doc__, "Fastforward Oid");
PyObject *
MergeResult_fastforward_oid__get__(MergeResult *self)
{
if (self->is_fastforward == 1)
{
Py_INCREF(self->fastforward_oid);
return self->fastforward_oid;
}
else
Py_RETURN_NONE;
}
PyDoc_STRVAR(MergeResult_status__doc__, "Merge repository status");
PyObject *
MergeResult_status__get__(MergeResult *self)
{
Py_INCREF(self->status);
return self->status;
}
PyGetSetDef MergeResult_getseters[] = {
GETTER(MergeResult, is_uptodate),
GETTER(MergeResult, is_fastforward),
GETTER(MergeResult, fastforward_oid),
GETTER(MergeResult, status),
{NULL},
};
PyDoc_STRVAR(MergeResult__doc__, "MergeResult object.");
PyTypeObject MergeResultType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.MergeResult", /* tp_name */
sizeof(MergeResult), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
MergeResult__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
MergeResult_getseters, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};

37
src/mergeresult.h Normal file
View File

@@ -0,0 +1,37 @@
/*
* Copyright 2010-2013 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.
*/
#ifndef INCLUDE_pygit2_merge_result_h
#define INCLUDE_pygit2_merge_result_h
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <git2.h>
PyObject* git_merge_result_to_python(git_merge_result *merge_result, Repository *repo);
#endif

View File

@@ -67,6 +67,7 @@ extern PyTypeObject NoteIterType;
extern PyTypeObject BlameType;
extern PyTypeObject BlameIterType;
extern PyTypeObject BlameHunkType;
extern PyTypeObject MergeResultType;
@@ -428,6 +429,10 @@ moduleinit(PyObject* m)
ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES)
ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES)
/* Merge */
INIT_TYPE(MergeResultType, NULL, NULL)
ADD_TYPE(m, MergeResult)
/* Global initialization of libgit2 */
git_threads_init();

View File

@@ -38,6 +38,7 @@
#include "remote.h"
#include "branch.h"
#include "blame.h"
#include "mergeresult.h"
#include <git2/odb_backend.h>
extern PyObject *GitError;
@@ -578,6 +579,58 @@ Repository_merge_base(Repository *self, PyObject *args)
return git_oid_to_python(&oid);
}
PyDoc_STRVAR(Repository_merge__doc__,
"merge(oid) -> MergeResult\n"
"\n"
"Merges the given oid and returns the MergeResult.\n"
"\n"
"If the merge is fastforward the MergeResult will contain the new\n"
"fastforward oid.\n"
"If the branch is uptodate, nothing to merge, the MergeResult will\n"
"have the fastforward oid as None.\n"
"If the merge is not fastforward the MergeResult will have the status\n"
"produced by the merge, even if there are conflicts.");
PyObject *
Repository_merge(Repository *self, PyObject *py_oid)
{
git_merge_result *merge_result;
git_merge_head *oid_merge_head;
git_oid oid;
const git_merge_opts default_opts = GIT_MERGE_OPTS_INIT;
int err;
size_t len;
PyObject *py_merge_result;
len = py_oid_to_git_oid(py_oid, &oid);
if (len == 0)
return NULL;
err = git_merge_head_from_oid(&oid_merge_head, self->repo, &oid);
if (err < 0)
goto error;
err = git_merge(&merge_result, self->repo,
(const git_merge_head **)&oid_merge_head, 1,
&default_opts);
if (err < 0)
{
git_merge_result_free(merge_result);
goto error;
}
py_merge_result = git_merge_result_to_python(merge_result, self);
git_merge_head_free(oid_merge_head);
git_merge_result_free(merge_result);
return py_merge_result;
error:
git_merge_head_free(oid_merge_head);
return Error_set(err);
}
PyDoc_STRVAR(Repository_walk__doc__,
"walk(oid, sort_mode) -> iterator\n"
"\n"
@@ -1093,7 +1146,7 @@ PyDoc_STRVAR(Repository_status__doc__,
"paths as keys and status flags as values. See pygit2.GIT_STATUS_*.");
PyObject *
Repository_status(Repository *self, PyObject *args)
Repository_status(Repository *self)
{
PyObject *dict;
int err;
@@ -1551,6 +1604,7 @@ PyMethodDef Repository_methods[] = {
METHOD(Repository, TreeBuilder, METH_VARARGS),
METHOD(Repository, walk, METH_VARARGS),
METHOD(Repository, merge_base, METH_VARARGS),
METHOD(Repository, merge, METH_O),
METHOD(Repository, read, METH_O),
METHOD(Repository, write, METH_VARARGS),
METHOD(Repository, create_reference_direct, METH_VARARGS),

View File

@@ -63,10 +63,12 @@ PyObject*
Repository_create_reference(Repository *self, PyObject *args, PyObject* kw);
PyObject* Repository_packall_references(Repository *self, PyObject *args);
PyObject* Repository_status(Repository *self, PyObject *args);
PyObject* Repository_status(Repository *self);
PyObject* Repository_status_file(Repository *self, PyObject *value);
PyObject* Repository_TreeBuilder(Repository *self, PyObject *args);
PyObject* Repository_blame(Repository *self, PyObject *args, PyObject *kwds);
PyObject* Repository_merge(Repository *self, PyObject *py_oid);
#endif

View File

@@ -217,5 +217,14 @@ typedef struct {
char boundary;
} BlameHunk;
/* git_merge */
typedef struct {
PyObject_HEAD
int is_uptodate;
int is_fastforward;
PyObject* fastforward_oid;
PyObject* status;
} MergeResult;
#endif

View File

@@ -302,6 +302,63 @@ class RepositoryTest_II(utils.RepoTestCase):
self.assertTrue("bonjour le monde\n" in diff.patch)
class RepositoryTest_III(utils.RepoTestCaseForMerging):
def test_merge_uptodate(self):
branch_head_hex = '5ebeeebb320790caf276b9fc8b24546d63316533'
branch_oid = self.repo.get(branch_head_hex).oid
merge_result = self.repo.merge(branch_oid)
self.assertTrue(merge_result.is_uptodate)
self.assertFalse(merge_result.is_fastforward)
self.assertEquals(None, merge_result.fastforward_oid)
self.assertEquals({}, merge_result.status)
def test_merge_fastforward(self):
branch_head_hex = 'e97b4cfd5db0fb4ebabf4f203979ca4e5d1c7c87'
branch_oid = self.repo.get(branch_head_hex).oid
merge_result = self.repo.merge(branch_oid)
self.assertFalse(merge_result.is_uptodate)
self.assertTrue(merge_result.is_fastforward)
# Asking twice to assure the reference counting is correct
self.assertEquals(branch_head_hex, merge_result.fastforward_oid.hex)
self.assertEquals(branch_head_hex, merge_result.fastforward_oid.hex)
self.assertEquals({}, merge_result.status)
def test_merge_no_fastforward_no_conflicts(self):
branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1'
branch_oid = self.repo.get(branch_head_hex).oid
merge_result = self.repo.merge(branch_oid)
self.assertFalse(merge_result.is_uptodate)
self.assertFalse(merge_result.is_fastforward)
self.assertEquals(None, merge_result.fastforward_oid)
# Asking twice to assure the reference counting is correct
self.assertEquals({'bye.txt': 1}, merge_result.status)
self.assertEquals({'bye.txt': 1}, merge_result.status)
def test_merge_no_fastforward_conflicts(self):
branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247'
branch_oid = self.repo.get(branch_head_hex).oid
merge_result = self.repo.merge(branch_oid)
self.assertFalse(merge_result.is_uptodate)
self.assertFalse(merge_result.is_fastforward)
self.assertEquals(None, merge_result.fastforward_oid)
# Asking twice to assure the reference counting is correct
self.assertEquals({'.gitignore': 132}, merge_result.status)
self.assertEquals({'.gitignore': 132}, merge_result.status)
def test_merge_invalid_hex(self):
branch_head_hex = '12345678'
self.assertRaises(KeyError, self.repo.merge, branch_head_hex)
def test_merge_already_something_in_index(self):
branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1'
branch_oid = self.repo.get(branch_head_hex).oid
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.merge, branch_oid)
class NewRepositoryTest(utils.NoRepoTestCase):
def test_new_repo(self):
@@ -376,8 +433,7 @@ class CloneRepositoryTest(utils.NoRepoTestCase):
def test_clone_remote_name(self):
repo_path = "./test/data/testrepo.git/"
repo = clone_repository(
repo_path, self._temp_dir, remote_name="custom_remote"
)
repo_path, self._temp_dir, remote_name="custom_remote")
self.assertFalse(repo.is_empty)
self.assertEqual(repo.remotes[0].name, "custom_remote")

View File

@@ -146,6 +146,11 @@ class RepoTestCase(AutoRepoTestCase):
repo_spec = 'tar', 'testrepo'
class RepoTestCaseForMerging(AutoRepoTestCase):
repo_spec = 'tar', 'testrepoformerging'
class DirtyRepoTestCase(AutoRepoTestCase):
repo_spec = 'tar', 'dirtyrepo'