]>
Commit | Line | Data |
---|---|---|
27df4275 MS |
1 | /* |
2 | * This file is free software; you can redistribute it and/or modify | |
3 | * it under the terms of the GNU General Public License, version 2, | |
4 | * as published by the Free Software Foundation. | |
5 | * | |
6 | * In addition to the permissions in the GNU General Public License, | |
7 | * the authors give you unlimited permission to link the compiled | |
8 | * version of this file into combinations with other programs, | |
9 | * and to distribute those combinations without any restriction | |
10 | * coming from the use of this file. (The General Public License | |
11 | * restrictions do apply in other respects; for example, they cover | |
12 | * modification of the file, and distribution when not linked into | |
13 | * a combined executable.) | |
14 | * | |
15 | * This file is distributed in the hope that it will be useful, but | |
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 | * General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License | |
21 | * along with this program; see the file COPYING. If not, write to | |
22 | * the Free Software Foundation, 51 Franklin Street, Fifth Floor, | |
23 | * Boston, MA 02110-1301, USA. | |
24 | */ | |
25 | ||
26 | #include "reflog.h" | |
27 | #include "repository.h" | |
28 | #include "filebuf.h" | |
29 | #include "signature.h" | |
30 | ||
31 | static int reflog_init(git_reflog **reflog, git_reference *ref) | |
32 | { | |
33 | git_reflog *log; | |
34 | ||
35 | *reflog = NULL; | |
36 | ||
37 | log = git__malloc(sizeof(git_reflog)); | |
38 | if (log == NULL) | |
39 | return GIT_ENOMEM; | |
40 | ||
41 | memset(log, 0x0, sizeof(git_reflog)); | |
42 | ||
43 | log->ref_name = git__strdup(ref->name); | |
44 | ||
45 | if (git_vector_init(&log->entries, 0, NULL) < 0) { | |
46 | free(log->ref_name); | |
47 | free(log); | |
48 | return GIT_ENOMEM; | |
49 | } | |
50 | ||
51 | *reflog = log; | |
52 | ||
53 | return GIT_SUCCESS; | |
54 | } | |
55 | ||
56 | static int reflog_write(git_repository *repo, const char *ref_name, | |
57 | const char *oid_old, const char *oid_new, | |
58 | const git_signature *committer, const char *msg) | |
59 | { | |
60 | int error; | |
61 | char log_path[GIT_PATH_MAX]; | |
62 | char *sig = NULL; | |
63 | git_filebuf log; | |
64 | ||
65 | assert(repo && ref_name && oid_old && oid_new && committer); | |
66 | ||
67 | git_path_join_n(log_path, 3, repo->path_repository, GIT_REFLOG_DIR, ref_name); | |
68 | ||
69 | if (git_futils_exists(log_path)) { | |
70 | if ((error = git_futils_mkpath2file(log_path)) < GIT_SUCCESS) | |
71 | return git__rethrow(error, "Failed to write reflog. Cannot create reflog directory"); | |
72 | ||
73 | } else if (git_futils_isfile(log_path)) | |
74 | return git__throw(GIT_ERROR, "Failed to write reflog. `%s` is directory", log_path); | |
75 | ||
76 | if ((error = git_filebuf_open(&log, log_path, GIT_FILEBUF_APPEND)) < GIT_SUCCESS) | |
77 | return git__throw(GIT_ERROR, "Failed to write reflog. Cannot open reflog `%s`", log_path); | |
78 | ||
79 | if ((error = git_signature__write(&sig, NULL, committer)) < GIT_SUCCESS) | |
80 | goto cleanup; | |
81 | ||
82 | sig[strlen(sig)-1] = '\0'; /* drop LF */ | |
83 | ||
84 | if ((error = git_filebuf_printf(&log, "%s %s %s", oid_old, oid_new, sig)) < GIT_SUCCESS) | |
85 | goto cleanup; | |
86 | ||
87 | if (msg) { | |
88 | if (strchr(msg, '\n')) { | |
89 | error = git__throw(GIT_ERROR, "msg must not contain newline"); | |
90 | goto cleanup; | |
91 | } | |
92 | ||
93 | if ((error = git_filebuf_printf(&log, "\t%s", msg)) < GIT_SUCCESS) | |
94 | goto cleanup; | |
95 | } | |
96 | ||
97 | error = git_filebuf_printf(&log, "\n"); | |
98 | ||
99 | cleanup: | |
100 | if (error < GIT_SUCCESS) | |
101 | git_filebuf_cleanup(&log); | |
102 | else | |
103 | error = git_filebuf_commit(&log); | |
104 | ||
105 | if (sig) | |
106 | free(sig); | |
107 | ||
108 | return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write reflog"); | |
109 | } | |
110 | ||
111 | static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size) | |
112 | { | |
113 | int error; | |
114 | const char *ptr; | |
115 | git_reflog_entry *entry; | |
116 | ||
117 | #define seek_forward(_increase) { \ | |
118 | if (_increase >= buf_size) \ | |
119 | return git__throw(GIT_ERROR, "Failed to seek forward. Buffer size exceeded"); \ | |
120 | buf += _increase; \ | |
121 | buf_size -= _increase; \ | |
122 | } | |
123 | ||
124 | while (buf_size > GIT_REFLOG_SIZE_MIN) { | |
125 | entry = git__malloc(sizeof(git_reflog_entry)); | |
126 | if (entry == NULL) | |
127 | return GIT_ENOMEM; | |
128 | ||
129 | entry->oid_old = git__strndup(buf, GIT_OID_HEXSZ); | |
130 | seek_forward(GIT_OID_HEXSZ+1); | |
131 | ||
132 | entry->oid_cur = git__strndup(buf, GIT_OID_HEXSZ); | |
133 | seek_forward(GIT_OID_HEXSZ+1); | |
134 | ||
135 | ptr = buf; | |
136 | ||
137 | /* Seek forward to the end of the signature. */ | |
138 | while (*buf && *buf != '\t' && *buf != '\n') | |
139 | seek_forward(1); | |
140 | ||
141 | entry->committer = git__malloc(sizeof(git_signature)); | |
142 | if (entry->committer == NULL) | |
143 | return GIT_ENOMEM; | |
144 | ||
145 | if ((error = git_signature__parse(entry->committer, &ptr, buf + buf_size, NULL)) < GIT_SUCCESS) | |
146 | goto cleanup; | |
147 | ||
148 | if (*buf == '\t') { | |
149 | /* We got a message. Read everything till we reach LF. */ | |
150 | seek_forward(1); | |
151 | entry->msg = (char *)buf; | |
152 | ||
153 | while (*buf && *buf != '\n') | |
154 | seek_forward(1); | |
155 | ||
156 | entry->msg = git__strndup(entry->msg, buf - entry->msg); | |
157 | } else | |
158 | entry->msg = NULL; | |
159 | ||
160 | while (*buf && *buf == '\n' && buf_size > 1) | |
161 | seek_forward(1); | |
162 | ||
163 | if ((error = git_vector_insert(&log->entries, entry)) < GIT_SUCCESS) | |
164 | goto cleanup; | |
165 | } | |
166 | ||
167 | #undef seek_forward | |
168 | ||
169 | cleanup: | |
170 | return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse reflog"); | |
171 | } | |
172 | ||
173 | void git_reflog_free(git_reflog *reflog) | |
174 | { | |
175 | unsigned int i; | |
176 | git_reflog_entry *entry; | |
177 | ||
178 | for (i=0; i < reflog->entries.length; i++) { | |
179 | entry = git_vector_get(&reflog->entries, i); | |
180 | ||
181 | free(entry->oid_old); | |
182 | free(entry->oid_cur); | |
183 | ||
184 | git_signature_free(entry->committer); | |
185 | ||
186 | free(entry->msg); | |
187 | free(entry); | |
188 | } | |
189 | ||
190 | git_vector_free(&reflog->entries); | |
191 | free(reflog->ref_name); | |
192 | free(reflog); | |
193 | } | |
194 | ||
195 | int git_reflog_read(git_reflog **reflog, git_reference *ref) | |
196 | { | |
197 | int error; | |
198 | char log_path[GIT_PATH_MAX]; | |
199 | git_fbuffer log_file = GIT_FBUFFER_INIT; | |
200 | git_reflog *log = NULL; | |
201 | ||
202 | *reflog = NULL; | |
203 | ||
204 | if ((error = reflog_init(&log, ref)) < GIT_SUCCESS) | |
205 | return git__rethrow(error, "Failed to read reflog. Cannot init reflog"); | |
206 | ||
207 | git_path_join_n(log_path, 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name); | |
208 | ||
209 | if ((error = git_futils_readbuffer(&log_file, log_path)) < GIT_SUCCESS) | |
210 | return git__rethrow(error, "Failed to read reflog. Cannot read file `%s`", log_path); | |
211 | ||
212 | error = reflog_parse(log, log_file.data, log_file.len); | |
213 | ||
214 | git_futils_freebuffer(&log_file); | |
215 | ||
216 | if (error == GIT_SUCCESS) | |
217 | *reflog = log; | |
218 | ||
219 | return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to read reflog"); | |
220 | } | |
221 | ||
222 | int git_reflog_write(git_reference *ref, const git_oid *oid_old, | |
223 | const git_signature *committer, const char *msg) | |
224 | { | |
225 | int error; | |
226 | char old[GIT_OID_HEXSZ+1]; | |
227 | char new[GIT_OID_HEXSZ+1]; | |
228 | git_reference *r; | |
229 | const git_oid *oid; | |
230 | ||
231 | if ((error = git_reference_resolve(&r, ref)) < GIT_SUCCESS) | |
232 | return git__rethrow(error, "Failed to write reflog. Cannot resolve reference `%s`", ref->name); | |
233 | ||
234 | oid = git_reference_oid(r); | |
235 | if (oid == NULL) | |
236 | return git__throw(GIT_ERROR, "Failed to write reflog. Cannot resolve reference `%s`", r->name); | |
237 | ||
238 | git_oid_to_string(new, GIT_OID_HEXSZ+1, oid); | |
239 | ||
240 | if (oid_old) | |
241 | git_oid_to_string(old, GIT_OID_HEXSZ+1, oid_old); | |
242 | else | |
243 | snprintf(old, GIT_OID_HEXSZ+1, "%0*d", GIT_OID_HEXSZ, 0); | |
244 | ||
245 | return reflog_write(ref->owner, ref->name, old, new, committer, msg); | |
246 | } | |
247 | ||
248 | unsigned int git_reflog_entrycount(git_reflog *reflog) | |
249 | { | |
250 | assert(reflog); | |
251 | return reflog->entries.length; | |
252 | } | |
253 | ||
254 | const git_reflog_entry * git_reflog_entry_byindex(git_reflog *reflog, unsigned int idx) | |
255 | { | |
256 | assert(reflog); | |
257 | return git_vector_get(&reflog->entries, idx); | |
258 | } | |
259 | ||
260 | char * git_reflog_entry_oidold(const git_reflog_entry *entry) | |
261 | { | |
262 | assert(entry); | |
263 | return entry->oid_old; | |
264 | } | |
265 | ||
266 | char * git_reflog_entry_oidnew(const git_reflog_entry *entry) | |
267 | { | |
268 | assert(entry); | |
269 | return entry->oid_cur; | |
270 | } | |
271 | ||
272 | git_signature * git_reflog_entry_committer(const git_reflog_entry *entry) | |
273 | { | |
274 | assert(entry); | |
275 | return entry->committer; | |
276 | } | |
277 | ||
278 | char * git_reflog_entry_msg(const git_reflog_entry *entry) | |
279 | { | |
280 | assert(entry); | |
281 | return entry->msg; | |
282 | } |