1 /*
2 * Copyright 2010-2021 The pygit2 contributors
3 *
4 * This file is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2,
6 * as published by the Free Software Foundation.
7 *
8 * In addition to the permissions in the GNU General Public License,
9 * the authors give you unlimited permission to link the compiled
10 * version of this file into combinations with other programs,
11 * and to distribute those combinations without any restriction
12 * coming from the use of this file. (The General Public License
13 * restrictions do apply in other respects; for example, they cover
14 * modification of the file, and distribution when not linked into
15 * a combined executable.)
16 *
17 * This file is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; see the file COPYING. If not, write to
24 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
25 * Boston, MA 02110-1301, USA.
26 */
27
28 #define PY_SSIZE_T_CLEAN
29 #include <Python.h>
30 #include "blob.h"
31 #include "diff.h"
32 #include "error.h"
33 #include "object.h"
34 #include "patch.h"
35 #include "utils.h"
36
37 extern PyObject *GitError;
38
39 extern PyTypeObject BlobType;
40
41 PyDoc_STRVAR(Blob_diff__doc__,
42 "diff([blob, flag, old_as_path, new_as_path])\n"
43 "\n"
44 "Directly generate a :py:class:`pygit2.Patch` from the difference\n"
45 "between two blobs.\n"
46 "\n"
47 "Returns: Patch.\n"
48 "\n"
49 "Parameters:\n"
50 "\n"
51 "blob : Blob\n"
52 " The :py:class:`~pygit2.Blob` to diff.\n"
53 "\n"
54 "flag\n"
55 " A GIT_DIFF_* constant.\n"
56 "\n"
57 "old_as_path : str\n"
58 " Treat old blob as if it had this filename.\n"
59 "\n"
60 "new_as_path : str\n"
61 " Treat new blob as if it had this filename.\n");
62
63 PyObject *
Blob_diff(Blob * self,PyObject * args,PyObject * kwds)64 Blob_diff(Blob *self, PyObject *args, PyObject *kwds)
65 {
66 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
67 git_patch *patch;
68 char *old_as_path = NULL, *new_as_path = NULL;
69 Blob *other = NULL;
70 int err;
71 char *keywords[] = {"blob", "flag", "old_as_path", "new_as_path", NULL};
72
73 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!Iss", keywords,
74 &BlobType, &other, &opts.flags,
75 &old_as_path, &new_as_path))
76 return NULL;
77
78 if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load
79 if (other && Object__load((Object*)other) == NULL) { return NULL; } // Lazy load
80
81 err = git_patch_from_blobs(&patch, self->blob, old_as_path,
82 other ? other->blob : NULL, new_as_path,
83 &opts);
84 if (err < 0)
85 return Error_set(err);
86
87 return wrap_patch(patch, self, other);
88 }
89
90
91 PyDoc_STRVAR(Blob_diff_to_buffer__doc__,
92 "diff_to_buffer([buffer, flag, old_as_path, buffer_as_path])\n"
93 "\n"
94 "Directly generate a :py:class:`~pygit2.Patch` from the difference\n"
95 "between a blob and a buffer.\n"
96 "\n"
97 "Returns: Patch.\n"
98 "\n"
99 "Parameters:\n"
100 "\n"
101 "buffer : Blob\n"
102 " Raw data for new side of diff.\n"
103 "\n"
104 "flag\n"
105 " A GIT_DIFF_* constant.\n"
106 "\n"
107 "old_as_path : str\n"
108 " Treat old blob as if it had this filename.\n"
109 "\n"
110 "buffer_as_path : str\n"
111 " Treat buffer as if it had this filename.\n");
112
113 PyObject *
Blob_diff_to_buffer(Blob * self,PyObject * args,PyObject * kwds)114 Blob_diff_to_buffer(Blob *self, PyObject *args, PyObject *kwds)
115 {
116 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
117 git_patch *patch;
118 char *old_as_path = NULL, *buffer_as_path = NULL;
119 const char *buffer = NULL;
120 Py_ssize_t buffer_len;
121 int err;
122 char *keywords[] = {"buffer", "flag", "old_as_path", "buffer_as_path",
123 NULL};
124
125 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|z#Iss", keywords,
126 &buffer, &buffer_len, &opts.flags,
127 &old_as_path, &buffer_as_path))
128 return NULL;
129
130 if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load
131
132 err = git_patch_from_blob_and_buffer(&patch, self->blob, old_as_path,
133 buffer, buffer_len, buffer_as_path,
134 &opts);
135 if (err < 0)
136 return Error_set(err);
137
138 return wrap_patch(patch, self, NULL);
139 }
140
141 static PyMethodDef Blob_methods[] = {
142 METHOD(Blob, diff, METH_VARARGS | METH_KEYWORDS),
143 METHOD(Blob, diff_to_buffer, METH_VARARGS | METH_KEYWORDS),
144 {NULL}
145 };
146
147
148 PyDoc_STRVAR(Blob_size__doc__,
149 "Size in bytes.\n"
150 "\n"
151 "Example:\n"
152 "\n"
153 " >>> print(blob.size)\n"
154 " 130\n");
155
156 PyObject *
Blob_size__get__(Blob * self)157 Blob_size__get__(Blob *self)
158 {
159 if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load
160 return PyLong_FromLongLong(git_blob_rawsize(self->blob));
161 }
162
163
164 PyDoc_STRVAR(Blob_is_binary__doc__, "True if binary data, False if not.");
165
166 PyObject *
Blob_is_binary__get__(Blob * self)167 Blob_is_binary__get__(Blob *self)
168 {
169 if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load
170
171 if (git_blob_is_binary(self->blob))
172 Py_RETURN_TRUE;
173 Py_RETURN_FALSE;
174 }
175
176
177 PyDoc_STRVAR(Blob_data__doc__,
178 "The contents of the blob, a byte string. This is the same as\n"
179 "Blob.read_raw().\n"
180 "\n"
181 "Example, print the contents of the ``.gitignore`` file:\n"
182 "\n"
183 " >>> blob = repo['d8022420bf6db02e906175f64f66676df539f2fd']\n"
184 " >>> print(blob.data)\n"
185 " MANIFEST\n"
186 " build\n"
187 " dist\n");
188
189 PyGetSetDef Blob_getseters[] = {
190 GETTER(Blob, size),
191 GETTER(Blob, is_binary),
192 {"data", (getter)Object_read_raw, NULL, Blob_data__doc__, NULL},
193 {NULL}
194 };
195
196 static int
Blob_getbuffer(Blob * self,Py_buffer * view,int flags)197 Blob_getbuffer(Blob *self, Py_buffer *view, int flags)
198 {
199 if (Object__load((Object*)self) == NULL) { return -1; } // Lazy load
200 return PyBuffer_FillInfo(view, (PyObject *) self,
201 (void *) git_blob_rawcontent(self->blob),
202 git_blob_rawsize(self->blob), 1, flags);
203 }
204
205 static PyBufferProcs Blob_as_buffer = {
206 (getbufferproc)Blob_getbuffer,
207 };
208
209 PyDoc_STRVAR(Blob__doc__, "Blob object.\n"
210 "\n"
211 "Blobs implement the buffer interface, which means you can get access\n"
212 "to its data via `memoryview(blob)` without the need to create a copy."
213 );
214
215 PyTypeObject BlobType = {
216 PyVarObject_HEAD_INIT(NULL, 0)
217 "_pygit2.Blob", /* tp_name */
218 sizeof(Blob), /* tp_basicsize */
219 0, /* tp_itemsize */
220 0, /* tp_dealloc */
221 0, /* tp_print */
222 0, /* tp_getattr */
223 0, /* tp_setattr */
224 0, /* tp_compare */
225 (reprfunc)Object_repr, /* tp_repr */
226 0, /* tp_as_number */
227 0, /* tp_as_sequence */
228 0, /* tp_as_mapping */
229 0, /* tp_hash */
230 0, /* tp_call */
231 0, /* tp_str */
232 0, /* tp_getattro */
233 0, /* tp_setattro */
234 &Blob_as_buffer, /* tp_as_buffer */
235 Py_TPFLAGS_DEFAULT, /* tp_flags */
236 Blob__doc__, /* tp_doc */
237 0, /* tp_traverse */
238 0, /* tp_clear */
239 0, /* tp_richcompare */
240 0, /* tp_weaklistoffset */
241 0, /* tp_iter */
242 0, /* tp_iternext */
243 Blob_methods, /* tp_methods */
244 0, /* tp_members */
245 Blob_getseters, /* tp_getset */
246 0, /* tp_base */
247 0, /* tp_dict */
248 0, /* tp_descr_get */
249 0, /* tp_descr_set */
250 0, /* tp_dictoffset */
251 0, /* tp_init */
252 0, /* tp_alloc */
253 0, /* tp_new */
254 };
255