Merge remote-tracking branch 'carlos/cffi-index'
This commit is contained in:
@@ -37,25 +37,14 @@ tree. You can use it independently of the index file, e.g.
|
|||||||
The Index type
|
The Index type
|
||||||
====================
|
====================
|
||||||
|
|
||||||
.. automethod:: pygit2.Index.add
|
.. autoclass:: pygit2.Index
|
||||||
.. automethod:: pygit2.Index.remove
|
:members:
|
||||||
.. automethod:: pygit2.Index.clear
|
|
||||||
.. automethod:: pygit2.Index.read
|
|
||||||
.. automethod:: pygit2.Index.write
|
|
||||||
.. automethod:: pygit2.Index.read_tree
|
|
||||||
.. automethod:: pygit2.Index.write_tree
|
|
||||||
.. automethod:: pygit2.Index.diff_to_tree
|
|
||||||
.. automethod:: pygit2.Index.diff_to_workdir
|
|
||||||
|
|
||||||
|
|
||||||
The IndexEntry type
|
The IndexEntry type
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
.. autoattribute:: pygit2.IndexEntry.id
|
.. autoclass:: pygit2.IndexEntry
|
||||||
.. autoattribute:: pygit2.IndexEntry.hex
|
:members:
|
||||||
.. autoattribute:: pygit2.IndexEntry.path
|
|
||||||
.. autoattribute:: pygit2.IndexEntry.mode
|
|
||||||
|
|
||||||
|
|
||||||
Status
|
Status
|
||||||
====================
|
====================
|
||||||
|
@@ -38,6 +38,7 @@ from .settings import Settings
|
|||||||
from .credentials import *
|
from .credentials import *
|
||||||
from .remote import Remote, get_credentials
|
from .remote import Remote, get_credentials
|
||||||
from .config import Config
|
from .config import Config
|
||||||
|
from .index import Index, IndexEntry
|
||||||
from .errors import check_error
|
from .errors import check_error
|
||||||
from .ffi import ffi, C, to_str
|
from .ffi import ffi, C, to_str
|
||||||
|
|
||||||
|
132
pygit2/decl.h
132
pygit2/decl.h
@@ -3,9 +3,11 @@ typedef ... git_remote;
|
|||||||
typedef ... git_refspec;
|
typedef ... git_refspec;
|
||||||
typedef ... git_push;
|
typedef ... git_push;
|
||||||
typedef ... git_cred;
|
typedef ... git_cred;
|
||||||
typedef ... git_diff_file;
|
|
||||||
typedef ... git_tree;
|
typedef ... git_tree;
|
||||||
typedef ... git_signature;
|
typedef ... git_signature;
|
||||||
|
typedef ... git_index;
|
||||||
|
typedef ... git_diff;
|
||||||
|
typedef ... git_index_conflict_iterator;
|
||||||
|
|
||||||
#define GIT_OID_RAWSZ ...
|
#define GIT_OID_RAWSZ ...
|
||||||
#define GIT_PATH_MAX ...
|
#define GIT_PATH_MAX ...
|
||||||
@@ -25,6 +27,7 @@ typedef struct git_strarray {
|
|||||||
size_t count;
|
size_t count;
|
||||||
} git_strarray;
|
} git_strarray;
|
||||||
|
|
||||||
|
typedef int64_t git_off_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GIT_OK = 0,
|
GIT_OK = 0,
|
||||||
@@ -181,6 +184,76 @@ int git_cred_ssh_key_new(
|
|||||||
const char *privatekey,
|
const char *privatekey,
|
||||||
const char *passphrase);
|
const char *passphrase);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* git_diff
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GIT_SUBMODULE_IGNORE_RESET = -1,
|
||||||
|
|
||||||
|
GIT_SUBMODULE_IGNORE_NONE = 1,
|
||||||
|
GIT_SUBMODULE_IGNORE_UNTRACKED = 2,
|
||||||
|
GIT_SUBMODULE_IGNORE_DIRTY = 3,
|
||||||
|
GIT_SUBMODULE_IGNORE_ALL = 4,
|
||||||
|
GIT_SUBMODULE_IGNORE_DEFAULT = 0
|
||||||
|
} git_submodule_ignore_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GIT_DELTA_UNMODIFIED = 0,
|
||||||
|
GIT_DELTA_ADDED = 1,
|
||||||
|
GIT_DELTA_DELETED = 2,
|
||||||
|
GIT_DELTA_MODIFIED = 3,
|
||||||
|
GIT_DELTA_RENAMED = 4,
|
||||||
|
GIT_DELTA_COPIED = 5,
|
||||||
|
GIT_DELTA_IGNORED = 6,
|
||||||
|
GIT_DELTA_UNTRACKED = 7,
|
||||||
|
GIT_DELTA_TYPECHANGE = 8,
|
||||||
|
} git_delta_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
git_oid id;
|
||||||
|
const char *path;
|
||||||
|
git_off_t size;
|
||||||
|
uint32_t flags;
|
||||||
|
uint16_t mode;
|
||||||
|
} git_diff_file;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
git_delta_t status;
|
||||||
|
uint32_t flags;
|
||||||
|
uint16_t similarity;
|
||||||
|
uint16_t nfiles;
|
||||||
|
git_diff_file old_file;
|
||||||
|
git_diff_file new_file;
|
||||||
|
} git_diff_delta;
|
||||||
|
|
||||||
|
typedef int (*git_diff_notify_cb)(
|
||||||
|
const git_diff *diff_so_far,
|
||||||
|
const git_diff_delta *delta_to_add,
|
||||||
|
const char *matched_pathspec,
|
||||||
|
void *payload);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned int version;
|
||||||
|
uint32_t flags;
|
||||||
|
|
||||||
|
git_submodule_ignore_t ignore_submodules;
|
||||||
|
git_strarray pathspec;
|
||||||
|
git_diff_notify_cb notify_cb;
|
||||||
|
void *notify_payload;
|
||||||
|
|
||||||
|
uint16_t context_lines;
|
||||||
|
uint16_t interhunk_lines;
|
||||||
|
uint16_t id_abbrev;
|
||||||
|
git_off_t max_size;
|
||||||
|
const char *old_prefix;
|
||||||
|
const char *new_prefix;
|
||||||
|
} git_diff_options;
|
||||||
|
|
||||||
|
int git_diff_init_options(git_diff_options *opts, unsigned int version);
|
||||||
|
int git_diff_index_to_workdir(git_diff **diff, git_repository *repo, git_index *index, const git_diff_options *opts);
|
||||||
|
int git_diff_tree_to_index(git_diff **diff, git_repository *repo, git_tree *old_tree, git_index *index, const git_diff_options *opts);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* git_checkout
|
* git_checkout
|
||||||
*/
|
*/
|
||||||
@@ -369,3 +442,60 @@ int git_repository_init_ext(
|
|||||||
git_repository **out,
|
git_repository **out,
|
||||||
const char *repo_path,
|
const char *repo_path,
|
||||||
git_repository_init_options *opts);
|
git_repository_init_options *opts);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* git_index
|
||||||
|
*/
|
||||||
|
typedef int64_t git_time_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
git_time_t seconds;
|
||||||
|
unsigned int nanoseconds;
|
||||||
|
} git_index_time;
|
||||||
|
|
||||||
|
typedef struct git_index_entry {
|
||||||
|
git_index_time ctime;
|
||||||
|
git_index_time mtime;
|
||||||
|
|
||||||
|
unsigned int dev;
|
||||||
|
unsigned int ino;
|
||||||
|
unsigned int mode;
|
||||||
|
unsigned int uid;
|
||||||
|
unsigned int gid;
|
||||||
|
git_off_t file_size;
|
||||||
|
|
||||||
|
git_oid id;
|
||||||
|
|
||||||
|
unsigned short flags;
|
||||||
|
unsigned short flags_extended;
|
||||||
|
|
||||||
|
const char *path;
|
||||||
|
} git_index_entry;
|
||||||
|
|
||||||
|
typedef int (*git_index_matched_path_cb)(
|
||||||
|
const char *path, const char *matched_pathspec, void *payload);
|
||||||
|
|
||||||
|
void git_index_free(git_index *index);
|
||||||
|
int git_repository_index(git_index **out, git_repository *repo);
|
||||||
|
int git_index_open(git_index **out, const char *index_path);
|
||||||
|
int git_index_read(git_index *index, int force);
|
||||||
|
int git_index_write(git_index *index);
|
||||||
|
size_t git_index_entrycount(const git_index *index);
|
||||||
|
int git_index_find(size_t *at_pos, git_index *index, const char *path);
|
||||||
|
int git_index_add_bypath(git_index *index, const char *path);
|
||||||
|
int git_index_add(git_index *index, const git_index_entry *source_entry);
|
||||||
|
int git_index_remove(git_index *index, const char *path, int stage);
|
||||||
|
int git_index_read_tree(git_index *index, const git_tree *tree);
|
||||||
|
int git_index_clear(git_index *index);
|
||||||
|
int git_index_write_tree(git_oid *out, git_index *index);
|
||||||
|
int git_index_write_tree_to(git_oid *out, git_index *index, git_repository *repo);
|
||||||
|
const git_index_entry *git_index_get_bypath(git_index *index, const char *path, int stage);
|
||||||
|
const git_index_entry *git_index_get_byindex(git_index *index, size_t n);
|
||||||
|
int git_index_add_all(git_index *index, const git_strarray *pathspec, unsigned int flags,
|
||||||
|
git_index_matched_path_cb callback, void *payload);
|
||||||
|
int git_index_has_conflicts(const git_index *index);
|
||||||
|
void git_index_conflict_iterator_free(git_index_conflict_iterator *iterator);
|
||||||
|
int git_index_conflict_iterator_new(git_index_conflict_iterator **iterator_out, git_index *index);
|
||||||
|
int git_index_conflict_get(const git_index_entry **ancestor_out, const git_index_entry **our_out, const git_index_entry **their_out, git_index *index, const char *path);
|
||||||
|
int git_index_conflict_next(const git_index_entry **ancestor_out, const git_index_entry **our_out, const git_index_entry **their_out, git_index_conflict_iterator *iterator);
|
||||||
|
int git_index_conflict_remove(git_index *index, const char *path);
|
||||||
|
428
pygit2/index.py
Normal file
428
pygit2/index.py
Normal file
@@ -0,0 +1,428 @@
|
|||||||
|
# -*- 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.
|
||||||
|
|
||||||
|
# Import from the future
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
from _pygit2 import Oid, Tree, Diff
|
||||||
|
|
||||||
|
from .ffi import ffi, C, to_str, is_string, strings_to_strarray
|
||||||
|
from .errors import check_error, GitError
|
||||||
|
|
||||||
|
class Index(object):
|
||||||
|
|
||||||
|
def __init__(self, path=None):
|
||||||
|
"""Create a new Index
|
||||||
|
|
||||||
|
If path is supplied, the read and write methods will use that path
|
||||||
|
to read from and write to.
|
||||||
|
"""
|
||||||
|
cindex = ffi.new('git_index **')
|
||||||
|
err = C.git_index_open(cindex, to_str(path))
|
||||||
|
check_error(err)
|
||||||
|
|
||||||
|
self._index = cindex[0]
|
||||||
|
self._cindex = cindex
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_c(cls, repo, ptr):
|
||||||
|
index = cls.__new__(cls);
|
||||||
|
index._repo = repo
|
||||||
|
index._index = ptr[0]
|
||||||
|
index._cindex = ptr
|
||||||
|
|
||||||
|
return index
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _pointer(self):
|
||||||
|
return bytes(ffi.buffer(self._cindex)[:])
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
C.git_index_free(self._index)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return C.git_index_entrycount(self._index)
|
||||||
|
|
||||||
|
def __contains__(self, path):
|
||||||
|
err = C.git_index_find(ffi.NULL, self._index, to_str(path))
|
||||||
|
if err == C.GIT_ENOTFOUND:
|
||||||
|
return False
|
||||||
|
|
||||||
|
check_error(err)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
centry = ffi.NULL
|
||||||
|
if is_string(key):
|
||||||
|
centry = C.git_index_get_bypath(self._index, to_str(key), 0)
|
||||||
|
elif not key >= 0:
|
||||||
|
raise ValueError(key)
|
||||||
|
else:
|
||||||
|
centry = C.git_index_get_byindex(self._index, key)
|
||||||
|
|
||||||
|
if centry == ffi.NULL:
|
||||||
|
raise KeyError(key)
|
||||||
|
|
||||||
|
return IndexEntry._from_c(centry)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return IndexIterator(self)
|
||||||
|
|
||||||
|
def read(self, force=True):
|
||||||
|
"""Update the contents the Index
|
||||||
|
|
||||||
|
Update the contents by reading from a file
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
force: if True (the default) allways reload. If False, only if the file has changed
|
||||||
|
"""
|
||||||
|
|
||||||
|
err = C.git_index_read(self._index, force)
|
||||||
|
check_error(err, True)
|
||||||
|
|
||||||
|
def write(self):
|
||||||
|
"""Write the contents of the Index to disk
|
||||||
|
"""
|
||||||
|
err = C.git_index_write(self._index)
|
||||||
|
check_error(err, True)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
err = C.git_index_clear(self._index)
|
||||||
|
check_error(err)
|
||||||
|
|
||||||
|
def read_tree(self, tree):
|
||||||
|
"""read_tree([Tree|Oid])
|
||||||
|
|
||||||
|
Replace the contents of the Index with those of a tree
|
||||||
|
|
||||||
|
The tree will be read recursively and all its children will also be
|
||||||
|
inserted into the Index.
|
||||||
|
"""
|
||||||
|
if is_string(tree):
|
||||||
|
tree = self._repo[tree]
|
||||||
|
|
||||||
|
if isinstance(tree, Oid):
|
||||||
|
if not hasattr(self, '_repo'):
|
||||||
|
raise TypeError("id given but no associated repository")
|
||||||
|
|
||||||
|
tree = self._repo[tree]
|
||||||
|
elif not isinstance(tree, Tree):
|
||||||
|
raise TypeError("argument must be Oid or Tree")
|
||||||
|
|
||||||
|
tree_cptr = ffi.new('git_tree **')
|
||||||
|
ffi.buffer(tree_cptr)[:] = tree._pointer[:]
|
||||||
|
err = C.git_index_read_tree(self._index, tree_cptr[0])
|
||||||
|
check_error(err)
|
||||||
|
|
||||||
|
def write_tree(self, repo=None):
|
||||||
|
"""write_tree([repo]) -> Oid
|
||||||
|
|
||||||
|
Create a tree out of the Index
|
||||||
|
|
||||||
|
The contents of the index will be written out to the object
|
||||||
|
database. If there is no associated repository, 'repo' must be
|
||||||
|
passed. If there is an associated repository and 'repo' is
|
||||||
|
passed, then that repository will be used instead.
|
||||||
|
|
||||||
|
It returns the id of the resulting tree.
|
||||||
|
"""
|
||||||
|
coid = ffi.new('git_oid *')
|
||||||
|
if repo:
|
||||||
|
err = C.git_index_write_tree_to(coid, self._index, repo._repo)
|
||||||
|
else:
|
||||||
|
err = C.git_index_write_tree(coid, self._index)
|
||||||
|
|
||||||
|
check_error(err)
|
||||||
|
return Oid(raw=bytes(ffi.buffer(coid)[:]))
|
||||||
|
|
||||||
|
|
||||||
|
def remove(self, path):
|
||||||
|
"""Remove an entry from the Index.
|
||||||
|
"""
|
||||||
|
err = C.git_index_remove(self._index, to_str(path), 0)
|
||||||
|
check_error(err, True)
|
||||||
|
|
||||||
|
def add_all(self, pathspecs=[]):
|
||||||
|
"""Add or update index entries matching files in the working directory.
|
||||||
|
|
||||||
|
If pathspecs are specified, only files matching those pathspecs will be added.
|
||||||
|
"""
|
||||||
|
arr, refs = strings_to_strarray(pathspecs)
|
||||||
|
err = C.git_index_add_all(self._index, arr, 0, ffi.NULL, ffi.NULL)
|
||||||
|
check_error(err, True)
|
||||||
|
|
||||||
|
def add(self, path_or_entry):
|
||||||
|
"""add([path|entry])
|
||||||
|
|
||||||
|
Add or update an entry in the Index
|
||||||
|
|
||||||
|
If a path is given, that file will be added. The path must be
|
||||||
|
relative to the root of the worktree and the Index must be associated
|
||||||
|
with a repository.
|
||||||
|
|
||||||
|
If an IndexEntry is given, that entry will be added or update in the Index
|
||||||
|
without checking for the existence of the path or id.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if is_string(path_or_entry):
|
||||||
|
path = path_or_entry
|
||||||
|
err = C.git_index_add_bypath(self._index, to_str(path))
|
||||||
|
elif isinstance(path_or_entry, IndexEntry):
|
||||||
|
entry = path_or_entry
|
||||||
|
centry, str_ref = entry._to_c()
|
||||||
|
err = C.git_index_add(self._index, centry)
|
||||||
|
else:
|
||||||
|
raise AttributeError('argument must be string or IndexEntry')
|
||||||
|
|
||||||
|
check_error(err, True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def has_conflicts(self):
|
||||||
|
"""Whether this Index contains conflict information
|
||||||
|
"""
|
||||||
|
return C.git_index_has_conflicts(self._index) != 0
|
||||||
|
|
||||||
|
def diff_to_workdir(self, flags=0, context_lines=3, interhunk_lines=0):
|
||||||
|
"""diff_to_workdir(flags=0, context_lines=3, interhunk_lines=0) -> Diff
|
||||||
|
|
||||||
|
Diff the index against the working directory
|
||||||
|
|
||||||
|
Return a :py:class:`~pygit2.Diff` object with the differences
|
||||||
|
between the index and the working copy.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
flags: a GIT_DIFF_* constant.
|
||||||
|
|
||||||
|
context_lines: the number of unchanged lines that define the
|
||||||
|
boundary of a hunk (and to display before and after)
|
||||||
|
|
||||||
|
interhunk_lines: the maximum number of unchanged lines between hunk
|
||||||
|
boundaries before the hunks will be merged into a one
|
||||||
|
"""
|
||||||
|
if not hasattr(self, '_repo'):
|
||||||
|
raise ValueError('diff needs an associated repository')
|
||||||
|
|
||||||
|
copts = ffi.new('git_diff_options *')
|
||||||
|
err = C.git_diff_init_options(copts, 1)
|
||||||
|
check_error(err)
|
||||||
|
|
||||||
|
copts.flags = flags
|
||||||
|
copts.context_lines = context_lines
|
||||||
|
copts.interhunk_lines = interhunk_lines
|
||||||
|
|
||||||
|
cdiff = ffi.new('git_diff **')
|
||||||
|
err = C.git_diff_index_to_workdir(cdiff, self._repo._repo, self._index, copts)
|
||||||
|
check_error(err)
|
||||||
|
|
||||||
|
return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), self._repo)
|
||||||
|
|
||||||
|
def diff_to_tree(self, tree, flags=0, context_lines=3, interhunk_lines=0):
|
||||||
|
"""diff_to_tree(flags=0, context_lines=3, interhunk_lines=0) -> Diff
|
||||||
|
|
||||||
|
Diff the index against a tree
|
||||||
|
|
||||||
|
Return a :py:class:`~pygit2.Diff` object with the differences between the
|
||||||
|
index and the given tree.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
tree: the tree to diff.
|
||||||
|
|
||||||
|
flags: a GIT_DIFF_* constant.
|
||||||
|
|
||||||
|
context_lines: the number of unchanged lines that define the boundary
|
||||||
|
of a hunk (and to display before and after)
|
||||||
|
|
||||||
|
interhunk_lines: the maximum number of unchanged lines between hunk
|
||||||
|
boundaries before the hunks will be merged into a one.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not hasattr(self, '_repo'):
|
||||||
|
raise ValueError('diff needs an associated repository')
|
||||||
|
|
||||||
|
if not isinstance(tree, Tree):
|
||||||
|
raise TypeError('tree must be a Tree')
|
||||||
|
|
||||||
|
copts = ffi.new('git_diff_options *')
|
||||||
|
err = C.git_diff_init_options(copts, 1)
|
||||||
|
check_error(err)
|
||||||
|
|
||||||
|
copts.flags = flags
|
||||||
|
copts.context_lines = context_lines
|
||||||
|
copts.interhunk_lines = interhunk_lines
|
||||||
|
|
||||||
|
ctree = ffi.new('git_tree **')
|
||||||
|
ffi.buffer(ctree)[:] = tree._pointer[:]
|
||||||
|
|
||||||
|
cdiff = ffi.new('git_diff **')
|
||||||
|
err = C.git_diff_tree_to_index(cdiff, self._repo._repo, ctree[0], self._index, copts)
|
||||||
|
check_error(err)
|
||||||
|
|
||||||
|
return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), self._repo)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def conflicts(self):
|
||||||
|
"""A collection of conflict information
|
||||||
|
|
||||||
|
This presents a mapping interface with the paths as keys. You
|
||||||
|
can use the ``del`` operator to remove a conflict form the Index.
|
||||||
|
|
||||||
|
Each conflict is made up of three elements. Access or iteration
|
||||||
|
of the conflicts returns a three-tuple of
|
||||||
|
:py:class:`~pygit2.IndexEntry`. The first is the common
|
||||||
|
ancestor, the second is the "ours" side of the conflict and the
|
||||||
|
thirs is the "theirs" side.
|
||||||
|
|
||||||
|
These elements may be None depending on which sides exist for
|
||||||
|
the particular conflict.
|
||||||
|
"""
|
||||||
|
if not hasattr(self, '_conflicts'):
|
||||||
|
self._conflicts = ConflictCollection(self)
|
||||||
|
|
||||||
|
return self._conflicts
|
||||||
|
|
||||||
|
class IndexEntry(object):
|
||||||
|
__slots__ = ['id', 'path', 'mode', '_index']
|
||||||
|
|
||||||
|
def __init__(self, path, object_id, mode):
|
||||||
|
self.path = path
|
||||||
|
"""The path of this entry"""
|
||||||
|
self.id = object_id
|
||||||
|
"""The id of the referenced object"""
|
||||||
|
self.mode = mode
|
||||||
|
"""The mode of this entry, a GIT_FILEMODE_ value"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hex(self):
|
||||||
|
"""The id of the referenced object as a hex string"""
|
||||||
|
return self.id.hex
|
||||||
|
|
||||||
|
def _to_c(self):
|
||||||
|
"""Convert this entry into the C structure
|
||||||
|
|
||||||
|
The first returned arg is the pointer, the second is the reference to
|
||||||
|
the string we allocated, which we need to exist past this function
|
||||||
|
"""
|
||||||
|
centry = ffi.new('git_index_entry *')
|
||||||
|
# basically memcpy()
|
||||||
|
ffi.buffer(ffi.addressof(centry, 'id'))[:] = self.id.raw[:]
|
||||||
|
centry.mode = self.mode
|
||||||
|
path = ffi.new('char[]', to_str(self.path))
|
||||||
|
centry.path = path
|
||||||
|
|
||||||
|
return centry, path
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _from_c(cls, centry):
|
||||||
|
if centry == ffi.NULL:
|
||||||
|
return None
|
||||||
|
|
||||||
|
entry = cls.__new__(cls)
|
||||||
|
entry.path = ffi.string(centry.path).decode()
|
||||||
|
entry.mode = centry.mode
|
||||||
|
entry.id = Oid(raw=bytes(ffi.buffer(ffi.addressof(centry, 'id'))[:]))
|
||||||
|
|
||||||
|
return entry
|
||||||
|
|
||||||
|
class IndexIterator(object):
|
||||||
|
|
||||||
|
def __init__(self, index):
|
||||||
|
self.index = index
|
||||||
|
self.n = 0
|
||||||
|
self.max = len(index)
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
return self.__next__()
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
if self.n >= self.max:
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
entry = self.index[self.n]
|
||||||
|
self.n += 1
|
||||||
|
|
||||||
|
return entry
|
||||||
|
|
||||||
|
class ConflictCollection(object):
|
||||||
|
|
||||||
|
def __init__(self, index):
|
||||||
|
self._index = index
|
||||||
|
|
||||||
|
def __getitem__(self, path):
|
||||||
|
cancestor = ffi.new('git_index_entry **')
|
||||||
|
cours = ffi.new('git_index_entry **')
|
||||||
|
ctheirs = ffi.new('git_index_entry **')
|
||||||
|
|
||||||
|
err = C.git_index_conflict_get(cancestor, cours, ctheirs, self._index._index, to_str(path))
|
||||||
|
check_error(err)
|
||||||
|
|
||||||
|
ancestor = IndexEntry._from_c(cancestor[0])
|
||||||
|
ours = IndexEntry._from_c(cours[0])
|
||||||
|
theirs = IndexEntry._from_c(ctheirs[0])
|
||||||
|
|
||||||
|
return ancestor, ours, theirs
|
||||||
|
|
||||||
|
def __delitem__(self, path):
|
||||||
|
err = C.git_index_conflict_remove(self._index._index, to_str(path))
|
||||||
|
check_error(err)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return ConflictIterator(self._index)
|
||||||
|
|
||||||
|
class ConflictIterator(object):
|
||||||
|
|
||||||
|
def __init__(self, index):
|
||||||
|
citer = ffi.new('git_index_conflict_iterator **')
|
||||||
|
err = C.git_index_conflict_iterator_new(citer, index._index)
|
||||||
|
check_error(err)
|
||||||
|
self._index = index
|
||||||
|
self._iter = citer[0]
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
C.git_index_conflict_iterator_free(self._iter)
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
return self.__next__()
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
cancestor = ffi.new('git_index_entry **')
|
||||||
|
cours = ffi.new('git_index_entry **')
|
||||||
|
ctheirs = ffi.new('git_index_entry **')
|
||||||
|
|
||||||
|
err = C.git_index_conflict_next(cancestor, cours, ctheirs, self._iter)
|
||||||
|
if err == C.GIT_ITEROVER:
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
check_error(err)
|
||||||
|
|
||||||
|
ancestor = IndexEntry._from_c(cancestor[0])
|
||||||
|
ours = IndexEntry._from_c(cours[0])
|
||||||
|
theirs = IndexEntry._from_c(ctheirs[0])
|
||||||
|
|
||||||
|
return ancestor, ours, theirs
|
@@ -39,6 +39,7 @@ from .ffi import ffi, C, to_str
|
|||||||
from .errors import check_error
|
from .errors import check_error
|
||||||
from .remote import Remote
|
from .remote import Remote
|
||||||
from .config import Config
|
from .config import Config
|
||||||
|
from .index import Index
|
||||||
|
|
||||||
class Repository(_Repository):
|
class Repository(_Repository):
|
||||||
|
|
||||||
@@ -307,3 +308,16 @@ class Repository(_Repository):
|
|||||||
etc.
|
etc.
|
||||||
"""
|
"""
|
||||||
C.git_repository_state_cleanup(self._repo)
|
C.git_repository_state_cleanup(self._repo)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Index
|
||||||
|
#
|
||||||
|
@property
|
||||||
|
def index(self):
|
||||||
|
"""Index representing the repository's index file
|
||||||
|
"""
|
||||||
|
cindex = ffi.new('git_index **')
|
||||||
|
err = C.git_repository_index(cindex, self._repo)
|
||||||
|
check_error(err, True)
|
||||||
|
|
||||||
|
return Index.from_c(self, cindex)
|
||||||
|
29
src/diff.c
29
src/diff.c
@@ -39,6 +39,7 @@ extern PyTypeObject TreeType;
|
|||||||
extern PyTypeObject IndexType;
|
extern PyTypeObject IndexType;
|
||||||
extern PyTypeObject DiffType;
|
extern PyTypeObject DiffType;
|
||||||
extern PyTypeObject HunkType;
|
extern PyTypeObject HunkType;
|
||||||
|
extern PyTypeObject RepositoryType;
|
||||||
|
|
||||||
PyTypeObject PatchType;
|
PyTypeObject PatchType;
|
||||||
|
|
||||||
@@ -383,6 +384,33 @@ PyTypeObject HunkType = {
|
|||||||
0, /* tp_new */
|
0, /* tp_new */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PyDoc_STRVAR(Diff_from_c__doc__, "Method exposed for Index to hook into");
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
Diff_from_c(Diff *dummy, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *py_diff, *py_repository;
|
||||||
|
git_diff *diff;
|
||||||
|
char *buffer;
|
||||||
|
Py_ssize_t length;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "OO!", &py_diff, &RepositoryType, &py_repository))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Here we need to do the opposite conversion from the _pointer getters */
|
||||||
|
if (PyBytes_AsStringAndSize(py_diff, &buffer, &length))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (length != sizeof(git_diff *)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "passed value is not a pointer");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the "buffer" contains the pointer */
|
||||||
|
diff = *((git_diff **) buffer);
|
||||||
|
|
||||||
|
return wrap_diff(diff, (Repository *) py_repository);
|
||||||
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(Diff_merge__doc__,
|
PyDoc_STRVAR(Diff_merge__doc__,
|
||||||
"merge(diff)\n"
|
"merge(diff)\n"
|
||||||
@@ -481,6 +509,7 @@ PyMappingMethods Diff_as_mapping = {
|
|||||||
static PyMethodDef Diff_methods[] = {
|
static PyMethodDef Diff_methods[] = {
|
||||||
METHOD(Diff, merge, METH_VARARGS),
|
METHOD(Diff, merge, METH_VARARGS),
|
||||||
METHOD(Diff, find_similar, METH_VARARGS),
|
METHOD(Diff, find_similar, METH_VARARGS),
|
||||||
|
METHOD(Diff, from_c, METH_STATIC | METH_VARARGS),
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
787
src/index.c
787
src/index.c
@@ -1,787 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define PY_SSIZE_T_CLEAN
|
|
||||||
#include <Python.h>
|
|
||||||
#include "error.h"
|
|
||||||
#include "types.h"
|
|
||||||
#include "utils.h"
|
|
||||||
#include "oid.h"
|
|
||||||
#include "diff.h"
|
|
||||||
#include "index.h"
|
|
||||||
|
|
||||||
extern PyTypeObject IndexType;
|
|
||||||
extern PyTypeObject TreeType;
|
|
||||||
extern PyTypeObject DiffType;
|
|
||||||
extern PyTypeObject IndexIterType;
|
|
||||||
extern PyTypeObject IndexEntryType;
|
|
||||||
extern PyTypeObject OidType;
|
|
||||||
extern PyTypeObject RepositoryType;
|
|
||||||
|
|
||||||
int
|
|
||||||
Index_init(Index *self, PyObject *args, PyObject *kwds)
|
|
||||||
{
|
|
||||||
char *path = NULL;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (kwds && PyDict_Size(kwds) > 0) {
|
|
||||||
PyErr_SetString(PyExc_TypeError, "Index takes no keyword arguments");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "|s", &path))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
self->repo = NULL;
|
|
||||||
err = git_index_open(&self->index, path);
|
|
||||||
if (err < 0) {
|
|
||||||
Error_set_str(err, path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Index_dealloc(Index* self)
|
|
||||||
{
|
|
||||||
PyObject_GC_UnTrack(self);
|
|
||||||
Py_XDECREF(self->repo);
|
|
||||||
git_index_free(self->index);
|
|
||||||
Py_TYPE(self)->tp_free(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
Index_traverse(Index *self, visitproc visit, void *arg)
|
|
||||||
{
|
|
||||||
Py_VISIT(self->repo);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyDoc_STRVAR(Index_add__doc__,
|
|
||||||
"add([path|entry])\n"
|
|
||||||
"\n"
|
|
||||||
"Add or update an index entry from a file in disk.");
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
Index_add(Index *self, PyObject *args)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
const char *path;
|
|
||||||
IndexEntry *py_entry;
|
|
||||||
|
|
||||||
if (PyArg_ParseTuple(args, "O!", &IndexEntryType, &py_entry)) {
|
|
||||||
err = git_index_add(self->index, &py_entry->entry);
|
|
||||||
if (err < 0)
|
|
||||||
return Error_set(err);
|
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
PyErr_Clear();
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "s", &path))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
err = git_index_add_bypath(self->index, path);
|
|
||||||
if (err < 0)
|
|
||||||
return Error_set_str(err, path);
|
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(Index_add_all__doc__,
|
|
||||||
"add_all([file names|glob pattern])\n"
|
|
||||||
"\n"
|
|
||||||
"Add or update index entries matching files in the working directory.");
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
Index_add_all(Index *self, PyObject *pylist)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
git_strarray pathspec;
|
|
||||||
|
|
||||||
if (get_strarraygit_from_pylist(&pathspec, pylist) < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
err = git_index_add_all(self->index, &pathspec, 0, NULL, NULL);
|
|
||||||
git_strarray_free(&pathspec);
|
|
||||||
|
|
||||||
if (err < 0) {
|
|
||||||
Error_set(err);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyDoc_STRVAR(Index_clear__doc__,
|
|
||||||
"clear()\n"
|
|
||||||
"\n"
|
|
||||||
"Clear the contents (all the entries) of an index object.");
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
Index_clear(Index *self)
|
|
||||||
{
|
|
||||||
git_index_clear(self->index);
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(Index_diff_to_workdir__doc__,
|
|
||||||
"diff_to_workdir([flag, context_lines, interhunk_lines]) -> Diff\n"
|
|
||||||
"\n"
|
|
||||||
"Return a :py:class:`~pygit2.Diff` object with the differences between the\n"
|
|
||||||
"index and the working copy.\n"
|
|
||||||
"\n"
|
|
||||||
"Arguments:\n"
|
|
||||||
"\n"
|
|
||||||
"flag: a GIT_DIFF_* constant.\n"
|
|
||||||
"\n"
|
|
||||||
"context_lines: the number of unchanged lines that define the boundary\n"
|
|
||||||
" of a hunk (and to display before and after)\n"
|
|
||||||
"\n"
|
|
||||||
"interhunk_lines: the maximum number of unchanged lines between hunk\n"
|
|
||||||
" boundaries before the hunks will be merged into a one.\n");
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
Index_diff_to_workdir(Index *self, PyObject *args)
|
|
||||||
{
|
|
||||||
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
|
||||||
git_diff *diff;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "|IHH", &opts.flags, &opts.context_lines,
|
|
||||||
&opts.interhunk_lines))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
err = git_diff_index_to_workdir(
|
|
||||||
&diff,
|
|
||||||
self->repo->repo,
|
|
||||||
self->index,
|
|
||||||
&opts);
|
|
||||||
|
|
||||||
if (err < 0)
|
|
||||||
return Error_set(err);
|
|
||||||
|
|
||||||
return wrap_diff(diff, self->repo);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyDoc_STRVAR(Index_diff_to_tree__doc__,
|
|
||||||
"diff_to_tree(tree [, flag, context_lines, interhunk_lines]) -> Diff\n"
|
|
||||||
"\n"
|
|
||||||
"Return a :py:class:`~pygit2.Diff` object with the differences between the\n"
|
|
||||||
"index and the given tree.\n"
|
|
||||||
"\n"
|
|
||||||
"Arguments:\n"
|
|
||||||
"\n"
|
|
||||||
"tree: the tree to diff.\n"
|
|
||||||
"\n"
|
|
||||||
"flag: a GIT_DIFF_* constant.\n"
|
|
||||||
"\n"
|
|
||||||
"context_lines: the number of unchanged lines that define the boundary\n"
|
|
||||||
" of a hunk (and to display before and after)\n"
|
|
||||||
"\n"
|
|
||||||
"interhunk_lines: the maximum number of unchanged lines between hunk\n"
|
|
||||||
" boundaries before the hunks will be merged into a one.\n");
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
Index_diff_to_tree(Index *self, PyObject *args)
|
|
||||||
{
|
|
||||||
Repository *py_repo;
|
|
||||||
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
|
||||||
git_diff *diff;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
Tree *py_tree = NULL;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "O!|IHH", &TreeType, &py_tree, &opts.flags,
|
|
||||||
&opts.context_lines, &opts.interhunk_lines))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
py_repo = py_tree->repo;
|
|
||||||
err = git_diff_tree_to_index(&diff, py_repo->repo, py_tree->tree,
|
|
||||||
self->index, &opts);
|
|
||||||
if (err < 0)
|
|
||||||
return Error_set(err);
|
|
||||||
|
|
||||||
return wrap_diff(diff, py_repo);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(Index__find__doc__,
|
|
||||||
"_find(path) -> integer\n"
|
|
||||||
"\n"
|
|
||||||
"Find the first index of any entries which point to given path in the\n"
|
|
||||||
"index file.");
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
Index__find(Index *self, PyObject *py_path)
|
|
||||||
{
|
|
||||||
char *path;
|
|
||||||
size_t idx;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
path = PyBytes_AsString(py_path);
|
|
||||||
if (!path)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
err = git_index_find(&idx, self->index, path);
|
|
||||||
if (err < 0)
|
|
||||||
return Error_set_str(err, path);
|
|
||||||
|
|
||||||
return PyLong_FromSize_t(idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(Index_read__doc__,
|
|
||||||
"read(force=True)\n"
|
|
||||||
"\n"
|
|
||||||
"Update the contents of an existing index object in memory by reading from\n"
|
|
||||||
"the hard disk."
|
|
||||||
"Arguments:\n"
|
|
||||||
"\n"
|
|
||||||
"force: if True (the default) allways reload. If False, only if the file has changed"
|
|
||||||
);
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
Index_read(Index *self, PyObject *args)
|
|
||||||
{
|
|
||||||
int err, force = 1;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "|i", &force))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
err = git_index_read(self->index, force);
|
|
||||||
if (err < GIT_OK)
|
|
||||||
return Error_set(err);
|
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(Index_write__doc__,
|
|
||||||
"write()\n"
|
|
||||||
"\n"
|
|
||||||
"Write an existing index object from memory back to disk using an atomic\n"
|
|
||||||
"file lock.");
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
Index_write(Index *self)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = git_index_write(self->index);
|
|
||||||
if (err < GIT_OK)
|
|
||||||
return Error_set(err);
|
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
Index_contains(Index *self, PyObject *value)
|
|
||||||
{
|
|
||||||
char *path;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
path = py_path_to_c_str(value);
|
|
||||||
if (!path)
|
|
||||||
return -1;
|
|
||||||
err = git_index_find(NULL, self->index, path);
|
|
||||||
if (err == GIT_ENOTFOUND) {
|
|
||||||
free(path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (err < 0) {
|
|
||||||
Error_set_str(err, path);
|
|
||||||
free(path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
free(path);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
Index_iter(Index *self)
|
|
||||||
{
|
|
||||||
IndexIter *iter;
|
|
||||||
|
|
||||||
iter = PyObject_New(IndexIter, &IndexIterType);
|
|
||||||
if (iter) {
|
|
||||||
Py_INCREF(self);
|
|
||||||
iter->owner = self;
|
|
||||||
iter->i = 0;
|
|
||||||
}
|
|
||||||
return (PyObject*)iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
Py_ssize_t
|
|
||||||
Index_len(Index *self)
|
|
||||||
{
|
|
||||||
return (Py_ssize_t)git_index_entrycount(self->index);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
wrap_index_entry(const git_index_entry *entry, Index *index)
|
|
||||||
{
|
|
||||||
IndexEntry *py_entry;
|
|
||||||
|
|
||||||
py_entry = PyObject_New(IndexEntry, &IndexEntryType);
|
|
||||||
if (!py_entry)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
memcpy(&py_entry->entry, entry, sizeof(struct git_index_entry));
|
|
||||||
py_entry->entry.path = strdup(entry->path);
|
|
||||||
if (!py_entry->entry.path) {
|
|
||||||
Py_CLEAR(py_entry);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (PyObject*)py_entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
Index_getitem(Index *self, PyObject *value)
|
|
||||||
{
|
|
||||||
long idx;
|
|
||||||
char *path;
|
|
||||||
const git_index_entry *index_entry;
|
|
||||||
|
|
||||||
/* Case 1: integer */
|
|
||||||
if (PyLong_Check(value)) {
|
|
||||||
idx = PyLong_AsLong(value);
|
|
||||||
if (idx == -1 && PyErr_Occurred())
|
|
||||||
return NULL;
|
|
||||||
if (idx < 0) {
|
|
||||||
PyErr_SetObject(PyExc_ValueError, value);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
index_entry = git_index_get_byindex(self->index, (size_t)idx);
|
|
||||||
/* Case 2: byte or text string */
|
|
||||||
} else {
|
|
||||||
path = py_path_to_c_str(value);
|
|
||||||
if (!path)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
index_entry = git_index_get_bypath(self->index, path, 0);
|
|
||||||
free(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!index_entry) {
|
|
||||||
PyErr_SetObject(PyExc_KeyError, value);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return wrap_index_entry(index_entry, self);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(Index_remove__doc__,
|
|
||||||
"remove(path)\n"
|
|
||||||
"\n"
|
|
||||||
"Removes an entry from index.");
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
Index_remove(Index *self, PyObject *args)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
const char *path;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "s", &path))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
err = git_index_remove(self->index, path, 0);
|
|
||||||
if (err < 0) {
|
|
||||||
Error_set(err);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(Index_read_tree__doc__,
|
|
||||||
"read_tree(tree)\n"
|
|
||||||
"\n"
|
|
||||||
"Update the index file from the specified tree. The tree can be a Tree object or an Oid.\n"
|
|
||||||
"Using an Oid is only possible if this index is associated with a repository");
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
Index_read_tree(Index *self, PyObject *value)
|
|
||||||
{
|
|
||||||
git_oid oid;
|
|
||||||
git_tree *tree = NULL;
|
|
||||||
int err, need_free = 0;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
len = py_oid_to_git_oid(value, &oid);
|
|
||||||
if (len == 0) {
|
|
||||||
Tree *py_tree;
|
|
||||||
if (!PyObject_TypeCheck(value, &TreeType)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyErr_Clear();
|
|
||||||
py_tree = (Tree *) value;
|
|
||||||
tree = py_tree->tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* if the user passed in an id but we're not associated with a
|
|
||||||
* repo, we can't do anything
|
|
||||||
*/
|
|
||||||
if (tree == NULL && self->repo == NULL) {
|
|
||||||
PyErr_SetString(PyExc_TypeError, "id given but no associated repository");
|
|
||||||
return NULL;
|
|
||||||
} else if (tree == NULL) {
|
|
||||||
need_free = 1;
|
|
||||||
err = git_tree_lookup_prefix(&tree, self->repo->repo, &oid, len);
|
|
||||||
if (err < 0)
|
|
||||||
return Error_set(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
err = git_index_read_tree(self->index, tree);
|
|
||||||
if (need_free)
|
|
||||||
git_tree_free(tree);
|
|
||||||
if (err < 0)
|
|
||||||
return Error_set(err);
|
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(Index_write_tree__doc__,
|
|
||||||
"write_tree([repo]) -> Oid\n"
|
|
||||||
"\n"
|
|
||||||
"Create a tree object from the index file, return its oid.\n"
|
|
||||||
"If 'repo' is passed, write to that repository's odb.");
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
Index_write_tree(Index *self, PyObject *args)
|
|
||||||
{
|
|
||||||
git_oid oid;
|
|
||||||
Repository *repo = NULL;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "|O!", &RepositoryType, &repo))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (repo)
|
|
||||||
err = git_index_write_tree_to(&oid, self->index, repo->repo);
|
|
||||||
else
|
|
||||||
err = git_index_write_tree(&oid, self->index);
|
|
||||||
|
|
||||||
if (err < 0)
|
|
||||||
return Error_set(err);
|
|
||||||
|
|
||||||
return git_oid_to_python(&oid);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyMethodDef Index_methods[] = {
|
|
||||||
METHOD(Index, add, METH_VARARGS),
|
|
||||||
METHOD(Index, add_all, METH_O),
|
|
||||||
METHOD(Index, remove, METH_VARARGS),
|
|
||||||
METHOD(Index, clear, METH_NOARGS),
|
|
||||||
METHOD(Index, diff_to_workdir, METH_VARARGS),
|
|
||||||
METHOD(Index, diff_to_tree, METH_VARARGS),
|
|
||||||
METHOD(Index, _find, METH_O),
|
|
||||||
METHOD(Index, read, METH_VARARGS),
|
|
||||||
METHOD(Index, write, METH_NOARGS),
|
|
||||||
METHOD(Index, read_tree, METH_O),
|
|
||||||
METHOD(Index, write_tree, METH_VARARGS),
|
|
||||||
{NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
PySequenceMethods Index_as_sequence = {
|
|
||||||
0, /* sq_length */
|
|
||||||
0, /* sq_concat */
|
|
||||||
0, /* sq_repeat */
|
|
||||||
0, /* sq_item */
|
|
||||||
0, /* sq_slice */
|
|
||||||
0, /* sq_ass_item */
|
|
||||||
0, /* sq_ass_slice */
|
|
||||||
(objobjproc)Index_contains, /* sq_contains */
|
|
||||||
};
|
|
||||||
|
|
||||||
PyMappingMethods Index_as_mapping = {
|
|
||||||
(lenfunc)Index_len, /* mp_length */
|
|
||||||
(binaryfunc)Index_getitem, /* mp_subscript */
|
|
||||||
NULL, /* mp_ass_subscript */
|
|
||||||
};
|
|
||||||
|
|
||||||
PyDoc_STRVAR(Index__doc__, "Index file.");
|
|
||||||
|
|
||||||
PyTypeObject IndexType = {
|
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
|
||||||
"_pygit2.Index", /* tp_name */
|
|
||||||
sizeof(Index), /* tp_basicsize */
|
|
||||||
0, /* tp_itemsize */
|
|
||||||
(destructor)Index_dealloc, /* tp_dealloc */
|
|
||||||
0, /* tp_print */
|
|
||||||
0, /* tp_getattr */
|
|
||||||
0, /* tp_setattr */
|
|
||||||
0, /* tp_compare */
|
|
||||||
0, /* tp_repr */
|
|
||||||
0, /* tp_as_number */
|
|
||||||
&Index_as_sequence, /* tp_as_sequence */
|
|
||||||
&Index_as_mapping, /* 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 |
|
|
||||||
Py_TPFLAGS_BASETYPE |
|
|
||||||
Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
|
||||||
Index__doc__, /* tp_doc */
|
|
||||||
(traverseproc)Index_traverse, /* tp_traverse */
|
|
||||||
0, /* tp_clear */
|
|
||||||
0, /* tp_richcompare */
|
|
||||||
0, /* tp_weaklistoffset */
|
|
||||||
(getiterfunc)Index_iter, /* tp_iter */
|
|
||||||
0, /* tp_iternext */
|
|
||||||
Index_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)Index_init, /* tp_init */
|
|
||||||
0, /* tp_alloc */
|
|
||||||
0, /* tp_new */
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
IndexIter_dealloc(IndexIter *self)
|
|
||||||
{
|
|
||||||
Py_CLEAR(self->owner);
|
|
||||||
Py_TYPE(self)->tp_free(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
IndexIter_iternext(IndexIter *self)
|
|
||||||
{
|
|
||||||
const git_index_entry *index_entry;
|
|
||||||
|
|
||||||
index_entry = git_index_get_byindex(self->owner->index, self->i);
|
|
||||||
if (!index_entry)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
self->i += 1;
|
|
||||||
return wrap_index_entry(index_entry, self->owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(IndexIter__doc__, "Index iterator.");
|
|
||||||
|
|
||||||
PyTypeObject IndexIterType = {
|
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
|
||||||
"_pygit2.IndexIter", /* tp_name */
|
|
||||||
sizeof(IndexIter), /* tp_basicsize */
|
|
||||||
0, /* tp_itemsize */
|
|
||||||
(destructor)IndexIter_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 | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
||||||
IndexIter__doc__, /* tp_doc */
|
|
||||||
0, /* tp_traverse */
|
|
||||||
0, /* tp_clear */
|
|
||||||
0, /* tp_richcompare */
|
|
||||||
0, /* tp_weaklistoffset */
|
|
||||||
PyObject_SelfIter, /* tp_iter */
|
|
||||||
(iternextfunc)IndexIter_iternext, /* tp_iternext */
|
|
||||||
};
|
|
||||||
|
|
||||||
int
|
|
||||||
IndexEntry_init(IndexEntry *self, PyObject *args, PyObject *kwds)
|
|
||||||
{
|
|
||||||
char *c_path = NULL;
|
|
||||||
Oid *id = NULL;
|
|
||||||
unsigned int mode;
|
|
||||||
char *keywords[] = {"path", "oid", "mode", NULL};
|
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO!I", keywords,
|
|
||||||
&c_path, &OidType, &id, &mode))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
memset(&self->entry, 0, sizeof(struct git_index_entry));
|
|
||||||
self->entry.path = strdup(c_path);
|
|
||||||
if (!self->entry.path)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (id)
|
|
||||||
git_oid_cpy(&self->entry.id, &id->oid);
|
|
||||||
|
|
||||||
if (mode)
|
|
||||||
self->entry.mode = mode;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IndexEntry_dealloc(IndexEntry *self)
|
|
||||||
{
|
|
||||||
free(self->entry.path);
|
|
||||||
PyObject_Del(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(IndexEntry_mode__doc__, "Mode.");
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
IndexEntry_mode__get__(IndexEntry *self)
|
|
||||||
{
|
|
||||||
return PyLong_FromLong(self->entry.mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
IndexEntry_mode__set__(IndexEntry *self, PyObject *py_mode)
|
|
||||||
{
|
|
||||||
long c_val;
|
|
||||||
|
|
||||||
c_val = PyLong_AsLong(py_mode);
|
|
||||||
if (c_val == -1 && PyErr_Occurred())
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
self->entry.mode = (unsigned int) c_val;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyDoc_STRVAR(IndexEntry_path__doc__, "Path.");
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
IndexEntry_path__get__(IndexEntry *self)
|
|
||||||
{
|
|
||||||
return to_path(self->entry.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
IndexEntry_path__set__(IndexEntry *self, PyObject *py_path)
|
|
||||||
{
|
|
||||||
char *c_path;
|
|
||||||
|
|
||||||
c_path = py_str_to_c_str(py_path, NULL);
|
|
||||||
if (!c_path)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
free(self->entry.path);
|
|
||||||
self->entry.path = c_path;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyDoc_STRVAR(IndexEntry_id__doc__, "Object id.");
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
IndexEntry_id__get__(IndexEntry *self)
|
|
||||||
{
|
|
||||||
return git_oid_to_python(&self->entry.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
IndexEntry_id__set__(IndexEntry *self, PyObject *py_id)
|
|
||||||
{
|
|
||||||
if (!py_oid_to_git_oid(py_id, &self->entry.id))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyDoc_STRVAR(IndexEntry_hex__doc__, "Hex id.");
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
IndexEntry_hex__get__(IndexEntry *self)
|
|
||||||
{
|
|
||||||
return git_oid_to_py_str(&self->entry.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyGetSetDef IndexEntry_getseters[] = {
|
|
||||||
GETSET(IndexEntry, mode),
|
|
||||||
GETSET(IndexEntry, path),
|
|
||||||
GETSET(IndexEntry, id),
|
|
||||||
GETTER(IndexEntry, hex),
|
|
||||||
{NULL},
|
|
||||||
};
|
|
||||||
|
|
||||||
PyDoc_STRVAR(IndexEntry__doc__, "Index entry.");
|
|
||||||
|
|
||||||
PyTypeObject IndexEntryType = {
|
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
|
||||||
"_pygit2.IndexEntry", /* tp_name */
|
|
||||||
sizeof(IndexEntry), /* tp_basicsize */
|
|
||||||
0, /* tp_itemsize */
|
|
||||||
(destructor)IndexEntry_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 */
|
|
||||||
IndexEntry__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 */
|
|
||||||
IndexEntry_getseters, /* tp_getset */
|
|
||||||
0, /* tp_base */
|
|
||||||
0, /* tp_dict */
|
|
||||||
0, /* tp_descr_get */
|
|
||||||
0, /* tp_descr_set */
|
|
||||||
0, /* tp_dictoffset */
|
|
||||||
(initproc)IndexEntry_init, /* tp_init */
|
|
||||||
0, /* tp_alloc */
|
|
||||||
0, /* tp_new */
|
|
||||||
};
|
|
48
src/index.h
48
src/index.h
@@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef INCLUDE_pygit2_index_h
|
|
||||||
#define INCLUDE_pygit2_index_h
|
|
||||||
|
|
||||||
#define PY_SSIZE_T_CLEAN
|
|
||||||
#include <Python.h>
|
|
||||||
#include <git2.h>
|
|
||||||
|
|
||||||
PyObject* Index_add(Index *self, PyObject *args);
|
|
||||||
PyObject* Index_add_all(Index *self, PyObject *pylist);
|
|
||||||
PyObject* Index_clear(Index *self);
|
|
||||||
PyObject* Index_find(Index *self, PyObject *py_path);
|
|
||||||
PyObject* Index_read(Index *self, PyObject *args);
|
|
||||||
PyObject* Index_write(Index *self);
|
|
||||||
PyObject* Index_iter(Index *self);
|
|
||||||
PyObject* Index_getitem(Index *self, PyObject *value);
|
|
||||||
PyObject* Index_read_tree(Index *self, PyObject *value);
|
|
||||||
PyObject* Index_write_tree(Index *self, PyObject *args);
|
|
||||||
Py_ssize_t Index_len(Index *self);
|
|
||||||
int Index_setitem(Index *self, PyObject *key, PyObject *value);
|
|
||||||
|
|
||||||
#endif
|
|
@@ -100,6 +100,14 @@ Object_type__get__(Object *self)
|
|||||||
return PyLong_FromLong(git_object_type(self->obj));
|
return PyLong_FromLong(git_object_type(self->obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(Object__pointer__doc__, "Get the object's pointer. For internal use only.");
|
||||||
|
PyObject *
|
||||||
|
Object__pointer__get__(Object *self)
|
||||||
|
{
|
||||||
|
/* Bytes means a raw buffer */
|
||||||
|
return PyBytes_FromStringAndSize((char *) &self->obj, sizeof(git_object *));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(Object_read_raw__doc__,
|
PyDoc_STRVAR(Object_read_raw__doc__,
|
||||||
"read_raw()\n"
|
"read_raw()\n"
|
||||||
@@ -181,6 +189,7 @@ PyGetSetDef Object_getseters[] = {
|
|||||||
GETTER(Object, id),
|
GETTER(Object, id),
|
||||||
GETTER(Object, hex),
|
GETTER(Object, hex),
|
||||||
GETTER(Object, type),
|
GETTER(Object, type),
|
||||||
|
GETTER(Object, _pointer),
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -52,9 +52,6 @@ extern PyTypeObject TreeEntryType;
|
|||||||
extern PyTypeObject TreeIterType;
|
extern PyTypeObject TreeIterType;
|
||||||
extern PyTypeObject BlobType;
|
extern PyTypeObject BlobType;
|
||||||
extern PyTypeObject TagType;
|
extern PyTypeObject TagType;
|
||||||
extern PyTypeObject IndexType;
|
|
||||||
extern PyTypeObject IndexEntryType;
|
|
||||||
extern PyTypeObject IndexIterType;
|
|
||||||
extern PyTypeObject WalkerType;
|
extern PyTypeObject WalkerType;
|
||||||
extern PyTypeObject ReferenceType;
|
extern PyTypeObject ReferenceType;
|
||||||
extern PyTypeObject RefLogIterType;
|
extern PyTypeObject RefLogIterType;
|
||||||
@@ -268,11 +265,6 @@ moduleinit(PyObject* m)
|
|||||||
/*
|
/*
|
||||||
* Index & Working copy
|
* Index & Working copy
|
||||||
*/
|
*/
|
||||||
INIT_TYPE(IndexType, NULL, PyType_GenericNew)
|
|
||||||
INIT_TYPE(IndexEntryType, NULL, PyType_GenericNew)
|
|
||||||
INIT_TYPE(IndexIterType, NULL, NULL)
|
|
||||||
ADD_TYPE(m, Index)
|
|
||||||
ADD_TYPE(m, IndexEntry)
|
|
||||||
/* Status */
|
/* Status */
|
||||||
ADD_CONSTANT_INT(m, GIT_STATUS_CURRENT)
|
ADD_CONSTANT_INT(m, GIT_STATUS_CURRENT)
|
||||||
ADD_CONSTANT_INT(m, GIT_STATUS_INDEX_NEW)
|
ADD_CONSTANT_INT(m, GIT_STATUS_INDEX_NEW)
|
||||||
|
@@ -461,41 +461,6 @@ Repository_write(Repository *self, PyObject *args)
|
|||||||
return git_oid_to_python(&oid);
|
return git_oid_to_python(&oid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(Repository_index__doc__, "Index file.");
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
Repository_index__get__(Repository *self, void *closure)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
git_index *index;
|
|
||||||
Index *py_index;
|
|
||||||
|
|
||||||
assert(self->repo);
|
|
||||||
|
|
||||||
if (self->index == NULL) {
|
|
||||||
err = git_repository_index(&index, self->repo);
|
|
||||||
if (err < 0)
|
|
||||||
return Error_set(err);
|
|
||||||
|
|
||||||
py_index = PyObject_GC_New(Index, &IndexType);
|
|
||||||
if (!py_index) {
|
|
||||||
git_index_free(index);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Py_INCREF(self);
|
|
||||||
py_index->repo = self;
|
|
||||||
py_index->index = index;
|
|
||||||
PyObject_GC_Track(py_index);
|
|
||||||
self->index = (PyObject*)py_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
Py_INCREF(self->index);
|
|
||||||
return self->index;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(Repository_path__doc__,
|
PyDoc_STRVAR(Repository_path__doc__,
|
||||||
"The normalized path to the git repository.");
|
"The normalized path to the git repository.");
|
||||||
|
|
||||||
@@ -1621,7 +1586,6 @@ PyMethodDef Repository_methods[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
PyGetSetDef Repository_getseters[] = {
|
PyGetSetDef Repository_getseters[] = {
|
||||||
GETTER(Repository, index),
|
|
||||||
GETTER(Repository, path),
|
GETTER(Repository, path),
|
||||||
GETSET(Repository, head),
|
GETSET(Repository, head),
|
||||||
GETTER(Repository, head_is_detached),
|
GETTER(Repository, head_is_detached),
|
||||||
|
36
src/tree.c
36
src/tree.c
@@ -395,19 +395,45 @@ Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds)
|
|||||||
{
|
{
|
||||||
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
||||||
git_diff *diff;
|
git_diff *diff;
|
||||||
|
git_index *index;
|
||||||
|
char *buffer;
|
||||||
|
Py_ssize_t length;
|
||||||
Repository *py_repo;
|
Repository *py_repo;
|
||||||
|
PyObject *py_idx, *py_idx_ptr;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
Index *py_idx = NULL;
|
if (!PyArg_ParseTuple(args, "O|IHH", &py_idx, &opts.flags,
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "O!|IHH", &IndexType, &py_idx, &opts.flags,
|
|
||||||
&opts.context_lines,
|
&opts.context_lines,
|
||||||
&opts.interhunk_lines))
|
&opts.interhunk_lines))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a hack to check whether we're passed an index, as I
|
||||||
|
* haven't found a good way to grab a type object for
|
||||||
|
* pygit2.index.Index.
|
||||||
|
*/
|
||||||
|
if (!PyObject_GetAttrString(py_idx, "_index")) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "argument must be an Index");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
py_idx_ptr = PyObject_GetAttrString(py_idx, "_pointer");
|
||||||
|
if (!py_idx_ptr)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Here we need to do the opposite conversion from the _pointer getters */
|
||||||
|
if (PyBytes_AsStringAndSize(py_idx_ptr, &buffer, &length))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (length != sizeof(git_index *)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "passed value is not a pointer");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the "buffer" contains the pointer */
|
||||||
|
index = *((git_index **) buffer);
|
||||||
|
|
||||||
py_repo = self->repo;
|
py_repo = self->repo;
|
||||||
err = git_diff_tree_to_index(&diff, py_repo->repo, self->tree,
|
err = git_diff_tree_to_index(&diff, py_repo->repo, self->tree, index, &opts);
|
||||||
py_idx->index, &opts);
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return Error_set(err);
|
return Error_set(err);
|
||||||
|
|
||||||
|
138
test/test_merge.py
Normal file
138
test/test_merge.py
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
# -*- 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 MergeTestBasic(utils.RepoTestCaseForMerging):
|
||||||
|
|
||||||
|
def test_merge_none(self):
|
||||||
|
self.assertRaises(TypeError, self.repo.merge, None)
|
||||||
|
|
||||||
|
def test_merge_analysis_uptodate(self):
|
||||||
|
branch_head_hex = '5ebeeebb320790caf276b9fc8b24546d63316533'
|
||||||
|
branch_id = self.repo.get(branch_head_hex).id
|
||||||
|
analysis, preference = self.repo.merge_analysis(branch_id)
|
||||||
|
|
||||||
|
self.assertTrue(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE)
|
||||||
|
self.assertFalse(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD)
|
||||||
|
self.assertEqual({}, self.repo.status())
|
||||||
|
|
||||||
|
def test_merge_analysis_fastforward(self):
|
||||||
|
branch_head_hex = 'e97b4cfd5db0fb4ebabf4f203979ca4e5d1c7c87'
|
||||||
|
branch_id = self.repo.get(branch_head_hex).id
|
||||||
|
analysis, preference = self.repo.merge_analysis(branch_id)
|
||||||
|
self.assertFalse(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE)
|
||||||
|
self.assertTrue(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD)
|
||||||
|
self.assertEqual({}, self.repo.status())
|
||||||
|
|
||||||
|
def test_merge_no_fastforward_no_conflicts(self):
|
||||||
|
branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1'
|
||||||
|
branch_id = self.repo.get(branch_head_hex).id
|
||||||
|
analysis, preference = self.repo.merge_analysis(branch_id)
|
||||||
|
self.assertFalse(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE)
|
||||||
|
self.assertFalse(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD)
|
||||||
|
# Asking twice to assure the reference counting is correct
|
||||||
|
self.assertEqual({}, self.repo.status())
|
||||||
|
self.assertEqual({}, self.repo.status())
|
||||||
|
|
||||||
|
def test_merge_no_fastforward_conflicts(self):
|
||||||
|
branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247'
|
||||||
|
branch_id = self.repo.get(branch_head_hex).id
|
||||||
|
|
||||||
|
analysis, preference = self.repo.merge_analysis(branch_id)
|
||||||
|
self.assertFalse(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE)
|
||||||
|
self.assertFalse(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD)
|
||||||
|
|
||||||
|
self.repo.merge(branch_id)
|
||||||
|
self.assertTrue(self.repo.index.has_conflicts)
|
||||||
|
status = pygit2.GIT_STATUS_WT_NEW | pygit2.GIT_STATUS_INDEX_DELETED
|
||||||
|
# Asking twice to assure the reference counting is correct
|
||||||
|
self.assertEqual({'.gitignore': status}, self.repo.status())
|
||||||
|
self.assertEqual({'.gitignore': status}, self.repo.status())
|
||||||
|
# Checking the index works as expected
|
||||||
|
self.repo.index.add('.gitignore')
|
||||||
|
self.repo.index.write()
|
||||||
|
self.assertEqual({'.gitignore': pygit2.GIT_STATUS_INDEX_MODIFIED}, self.repo.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).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.merge, branch_oid)
|
||||||
|
|
||||||
|
class MergeTestWithConflicts(utils.RepoTestCaseForMerging):
|
||||||
|
|
||||||
|
def test_merge_no_fastforward_conflicts(self):
|
||||||
|
branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247'
|
||||||
|
branch_id = self.repo.get(branch_head_hex).id
|
||||||
|
|
||||||
|
analysis, preference = self.repo.merge_analysis(branch_id)
|
||||||
|
self.assertFalse(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE)
|
||||||
|
self.assertFalse(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD)
|
||||||
|
|
||||||
|
self.repo.merge(branch_id)
|
||||||
|
self.assertTrue(self.repo.index.has_conflicts)
|
||||||
|
self.assertRaises(KeyError, self.repo.index.conflicts.__getitem__, 'some-file')
|
||||||
|
ancestor, ours, theirs = self.repo.index.conflicts['.gitignore']
|
||||||
|
self.assertEqual(None, ancestor)
|
||||||
|
self.assertNotEqual(None, ours)
|
||||||
|
self.assertNotEqual(None, theirs)
|
||||||
|
self.assertEqual('.gitignore', ours.path)
|
||||||
|
self.assertEqual('.gitignore', theirs.path)
|
||||||
|
self.assertEqual(1, len(list(self.repo.index.conflicts)))
|
||||||
|
# Checking the index works as expected
|
||||||
|
self.repo.index.add('.gitignore')
|
||||||
|
self.repo.index.write()
|
||||||
|
self.assertRaises(KeyError, self.repo.index.conflicts.__getitem__, '.gitignore')
|
||||||
|
|
||||||
|
def test_merge_remove_conflicts(self):
|
||||||
|
other_branch_tip = '1b2bae55ac95a4be3f8983b86cd579226d0eb247'
|
||||||
|
self.repo.merge(other_branch_tip)
|
||||||
|
idx = self.repo.index
|
||||||
|
self.assertTrue(idx.has_conflicts)
|
||||||
|
self.assertRaises(KeyError, idx.conflicts.__delitem__, 'some-file')
|
||||||
|
del idx.conflicts['.gitignore']
|
||||||
|
self.assertFalse(idx.has_conflicts)
|
@@ -40,8 +40,6 @@ from os.path import join, realpath
|
|||||||
|
|
||||||
# Import from pygit2
|
# Import from pygit2
|
||||||
from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT
|
from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT
|
||||||
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
|
|
||||||
from pygit2 import init_repository, clone_repository, clone_into, discover_repository
|
from pygit2 import init_repository, clone_repository, clone_into, discover_repository
|
||||||
from pygit2 import Oid, Reference, hashfile
|
from pygit2 import Oid, Reference, hashfile
|
||||||
import pygit2
|
import pygit2
|
||||||
@@ -318,69 +316,6 @@ class RepositoryTest_II(utils.RepoTestCase):
|
|||||||
self.assertTrue("hola mundo\n" in diff.patch)
|
self.assertTrue("hola mundo\n" in diff.patch)
|
||||||
self.assertTrue("bonjour le monde\n" in diff.patch)
|
self.assertTrue("bonjour le monde\n" in diff.patch)
|
||||||
|
|
||||||
|
|
||||||
class RepositoryTest_III(utils.RepoTestCaseForMerging):
|
|
||||||
|
|
||||||
def test_merge_none(self):
|
|
||||||
self.assertRaises(TypeError, self.repo.merge, None)
|
|
||||||
|
|
||||||
def test_merge_analysis_uptodate(self):
|
|
||||||
branch_head_hex = '5ebeeebb320790caf276b9fc8b24546d63316533'
|
|
||||||
branch_id = self.repo.get(branch_head_hex).id
|
|
||||||
analysis, preference = self.repo.merge_analysis(branch_id)
|
|
||||||
|
|
||||||
self.assertTrue(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE)
|
|
||||||
self.assertFalse(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD)
|
|
||||||
self.assertEqual({}, self.repo.status())
|
|
||||||
|
|
||||||
def test_merge_analysis_fastforward(self):
|
|
||||||
branch_head_hex = 'e97b4cfd5db0fb4ebabf4f203979ca4e5d1c7c87'
|
|
||||||
branch_id = self.repo.get(branch_head_hex).id
|
|
||||||
analysis, preference = self.repo.merge_analysis(branch_id)
|
|
||||||
self.assertFalse(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE)
|
|
||||||
self.assertTrue(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD)
|
|
||||||
self.assertEqual({}, self.repo.status())
|
|
||||||
|
|
||||||
def test_merge_no_fastforward_no_conflicts(self):
|
|
||||||
branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1'
|
|
||||||
branch_id = self.repo.get(branch_head_hex).id
|
|
||||||
analysis, preference = self.repo.merge_analysis(branch_id)
|
|
||||||
self.assertFalse(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE)
|
|
||||||
self.assertFalse(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD)
|
|
||||||
# Asking twice to assure the reference counting is correct
|
|
||||||
self.assertEqual({}, self.repo.status())
|
|
||||||
self.assertEqual({}, self.repo.status())
|
|
||||||
|
|
||||||
def test_merge_no_fastforward_conflicts(self):
|
|
||||||
branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247'
|
|
||||||
branch_id = self.repo.get(branch_head_hex).id
|
|
||||||
|
|
||||||
analysis, preference = self.repo.merge_analysis(branch_id)
|
|
||||||
self.assertFalse(analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE)
|
|
||||||
self.assertFalse(analysis & GIT_MERGE_ANALYSIS_FASTFORWARD)
|
|
||||||
|
|
||||||
self.repo.merge(branch_id)
|
|
||||||
status = pygit2.GIT_STATUS_WT_NEW | pygit2.GIT_STATUS_INDEX_DELETED
|
|
||||||
# Asking twice to assure the reference counting is correct
|
|
||||||
self.assertEqual({'.gitignore': status}, self.repo.status())
|
|
||||||
self.assertEqual({'.gitignore': status}, self.repo.status())
|
|
||||||
# Checking the index works as expected
|
|
||||||
self.repo.index.add('.gitignore')
|
|
||||||
self.repo.index.write()
|
|
||||||
self.assertEqual({'.gitignore': pygit2.GIT_STATUS_INDEX_MODIFIED}, self.repo.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).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.merge, branch_oid)
|
|
||||||
|
|
||||||
class RepositorySignatureTest(utils.RepoTestCase):
|
class RepositorySignatureTest(utils.RepoTestCase):
|
||||||
|
|
||||||
def test_default_signature(self):
|
def test_default_signature(self):
|
||||||
|
Reference in New Issue
Block a user