Skip to content

Add mailmap support to pygit2 #804

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .travis.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

cd ~

git clone --depth=1 -b maint/v0.27 https://github.com/libgit2/libgit2.git
# XXX: Test against master to use mailmap APIs.
git clone --depth=1 -b master https://github.com/libgit2/libgit2.git
cd libgit2/

mkdir build && cd build
Expand Down
3 changes: 2 additions & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ init:
build_script:
- cmd: |
set LIBGIT2=%APPVEYOR_BUILD_FOLDER%\build\libgit2
git clone --depth=1 -b maint/v0.27 https://github.com/libgit2/libgit2.git libgit2
# Test against master to use Mailmap APIs
git clone --depth=1 -b master https://github.com/libgit2/libgit2.git libgit2
mkdir build

cd build
Expand Down
1 change: 1 addition & 0 deletions pygit2/decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,7 @@ typedef enum {
typedef struct git_config_entry {
const char *name;
const char *value;
unsigned int include_depth;
git_config_level_t level;
void (*free)(struct git_config_entry *entry);
void *payload;
Expand Down
258 changes: 258 additions & 0 deletions src/mailmap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
/*
* Copyright 2010-2015 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 <structmember.h>
#include "error.h"
#include "utils.h"
#include "types.h"
#include "mailmap.h"
#include "signature.h"

extern PyTypeObject SignatureType;
extern PyTypeObject RepositoryType;

int
Mailmap_init(Mailmap *self, PyObject *args, PyObject *kwargs)
{
char *keywords[] = {NULL};
git_mailmap *mm;
int error;

/* Our init method does not handle parameters */
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", keywords))
return -1;

error = git_mailmap_new(&mm);
if (error < 0) {
Error_set(error);
return -1;
}

self->mailmap = mm;
return 0;
}

PyDoc_STRVAR(Mailmap_from_repository__doc__,
"from_repository(repository: Repository) -> Mailmap\n"
"\n"
"Create a new mailmap instance from a repository, loading mailmap files based on the repository's configuration.\n"
"\n"
"Mailmaps are loaded in the following order:\n"
" 1. '.mailmap' in the root of the repository's working directory, if present.\n"
" 2. The blob object identified by the 'mailmap.blob' config entry, if set.\n"
" [NOTE: 'mailmap.blob' defaults to 'HEAD:.mailmap' in bare repositories]\n"
" 3. The path in the 'mailmap.file' config entry, if set.");
PyObject *
Mailmap_from_repository(Mailmap *dummy, PyObject *args)
{
Repository *repo = NULL;
git_mailmap *mm = NULL;
int error;

if (!PyArg_ParseTuple(args, "O!", &RepositoryType, &repo))
return NULL;

error = git_mailmap_from_repository(&mm, repo->repo);
if (error < 0)
return Error_set(error);

return wrap_mailmap(mm);
}

PyDoc_STRVAR(Mailmap_from_buffer__doc__,
"from_buffer(buffer: str) -> Mailmap\n"
"\n"
"Parse a passed-in buffer and construct a mailmap object.");
PyObject *
Mailmap_from_buffer(Mailmap *dummy, PyObject *args)
{
char *buffer = NULL;
Py_ssize_t size = 0;
git_mailmap *mm = NULL;
int error;

if (!PyArg_ParseTuple(args, "s#", &buffer, &size))
return NULL;

error = git_mailmap_from_buffer(&mm, buffer, size);
if (error < 0)
return Error_set(error);

return wrap_mailmap(mm);
}

PyDoc_STRVAR(Mailmap_add_entry__doc__,
"add_entry(real_name=None, real_email=None, replace_name=None, replace_email)\n"
"\n"
"Add a new entry to the mailmap, overriding existing entries.");
PyObject *
Mailmap_add_entry(Mailmap *self, PyObject *args, PyObject *kwargs)
{
char *keywords[] = {"real_name", "real_email", "replace_name", "replace_email", NULL};
char *real_name = NULL, *real_email = NULL;
char *replace_name = NULL, *replace_email = NULL;
int error;

if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|zzzs", keywords,
&real_name, &real_email,
&replace_name, &replace_email))
return NULL;

/* replace_email cannot be null */
if (!replace_email) {
PyErr_BadArgument();
return NULL;
}

error = git_mailmap_add_entry(self->mailmap, real_name, real_email,
replace_name, replace_email);
if (error < 0)
return Error_set(error);

Py_RETURN_NONE;
}

PyDoc_STRVAR(Mailmap_resolve__doc__,
"resolve(name: str, email: str) -> (str, str)\n"
"\n"
"Resolve name & email to a real name and email.");
PyObject *
Mailmap_resolve(Mailmap *self, PyObject *args)
{
const char *name = NULL, *email = NULL;
const char *real_name = NULL, *real_email = NULL;
int error;

if (!PyArg_ParseTuple(args, "ss", &name, &email))
return NULL;

error = git_mailmap_resolve(&real_name, &real_email, self->mailmap, name, email);
if (error < 0)
return Error_set(error);

return Py_BuildValue("ss", real_name, real_email);
}

PyDoc_STRVAR(Mailmap_resolve_signature__doc__,
"resolve_signature(sig: Signature) -> Signature\n"
"\n"
"Resolve signature to real name and email.");
PyObject *
Mailmap_resolve_signature(Mailmap *self, PyObject *args)
{
Signature *sig = NULL;
git_signature *resolved = NULL;
int error;

if (!PyArg_ParseTuple(args, "O!", &SignatureType, &sig))
return NULL;

error = git_mailmap_resolve_signature(&resolved, self->mailmap, sig->signature);
if (error < 0)
return Error_set(error);

return build_signature(sig->obj, resolved, sig->encoding);
}

static void
Mailmap_dealloc(Mailmap *self)
{
git_mailmap_free(self->mailmap);
PyObject_Del(self);
}


PyMethodDef Mailmap_methods[] = {
METHOD(Mailmap, add_entry, METH_VARARGS | METH_KEYWORDS),
METHOD(Mailmap, resolve, METH_VARARGS),
METHOD(Mailmap, resolve_signature, METH_VARARGS),
METHOD(Mailmap, from_repository, METH_VARARGS | METH_STATIC),
METHOD(Mailmap, from_buffer, METH_VARARGS | METH_STATIC),
{NULL}
};


PyDoc_STRVAR(Mailmap__doc__, "Mailmap object.");

PyTypeObject MailmapType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.Mailmap", /* tp_name */
sizeof(Mailmap), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)Mailmap_dealloc, /* 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 */
Mailmap__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
Mailmap_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)Mailmap_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};

PyObject *
wrap_mailmap(git_mailmap* mm)
{
Mailmap* py_mm = NULL;

py_mm = PyObject_New(Mailmap, &MailmapType);
if (py_mm == NULL) {
PyErr_NoMemory();
return NULL;
}

py_mm->mailmap = mm;

return (PyObject*) py_mm;
}
38 changes: 38 additions & 0 deletions src/mailmap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2017 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_mailmap_h
#define INCLUDE_pygit2_mailmap_h

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <git2.h>
#include "types.h"

PyObject* wrap_mailmap(git_mailmap *c_object);

#endif
5 changes: 5 additions & 0 deletions src/pygit2.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ extern PyTypeObject RefspecType;
extern PyTypeObject NoteType;
extern PyTypeObject NoteIterType;
extern PyTypeObject WorktreeType;
extern PyTypeObject MailmapType;


PyDoc_STRVAR(discover_repository__doc__,
Expand Down Expand Up @@ -454,6 +455,10 @@ moduleinit(PyObject* m)
ADD_CONSTANT_INT(m, GIT_STASH_APPLY_DEFAULT);
ADD_CONSTANT_INT(m, GIT_STASH_APPLY_REINSTATE_INDEX);

/* Mailmap */
INIT_TYPE(MailmapType, NULL, PyType_GenericNew)
ADD_TYPE(m, Mailmap)

/* Global initialization of libgit2 */
git_libgit2_init();

Expand Down
6 changes: 6 additions & 0 deletions src/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,10 @@ typedef struct {
char *encoding;
} Signature;

/* git_mailmap */
typedef struct {
PyObject_HEAD
git_mailmap *mailmap;
} Mailmap;

#endif
2 changes: 2 additions & 0 deletions src/worktree.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@
#include <git2.h>
#include <git2/worktree.h>

/* git_worktree is an internal unstable datastructure (#803) */
struct git_worktree {
char *name;
char *worktree_path;
char *gitlink_path;
char *gitdir_path;
char *commondir_path;
Expand Down
Loading