1 /*
2  * Copyright (C) the libgit2 contributors. All rights reserved.
3  *
4  * This file is part of libgit2, distributed under the GNU GPL v2 with
5  * a Linking Exception. For full terms see the included COPYING file.
6  */
7 
8 #include "reflog.h"
9 
10 #include "repository.h"
11 #include "filebuf.h"
12 #include "signature.h"
13 #include "refdb.h"
14 
15 #include <git2/sys/refdb_backend.h>
16 
git_reflog_entry__alloc(void)17 git_reflog_entry *git_reflog_entry__alloc(void)
18 {
19 	return git__calloc(1, sizeof(git_reflog_entry));
20 }
21 
git_reflog_entry__free(git_reflog_entry * entry)22 void git_reflog_entry__free(git_reflog_entry *entry)
23 {
24 	git_signature_free(entry->committer);
25 
26 	git__free(entry->msg);
27 	git__free(entry);
28 }
29 
git_reflog_free(git_reflog * reflog)30 void git_reflog_free(git_reflog *reflog)
31 {
32 	size_t i;
33 	git_reflog_entry *entry;
34 
35 	if (reflog == NULL)
36 		return;
37 
38 	if (reflog->db)
39 		GIT_REFCOUNT_DEC(reflog->db, git_refdb__free);
40 
41 	for (i=0; i < reflog->entries.length; i++) {
42 		entry = git_vector_get(&reflog->entries, i);
43 
44 		git_reflog_entry__free(entry);
45 	}
46 
47 	git_vector_free(&reflog->entries);
48 	git__free(reflog->ref_name);
49 	git__free(reflog);
50 }
51 
git_reflog_read(git_reflog ** reflog,git_repository * repo,const char * name)52 int git_reflog_read(git_reflog **reflog, git_repository *repo,  const char *name)
53 {
54 	git_refdb *refdb;
55 	int error;
56 
57 	assert(reflog && repo && name);
58 
59 	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
60 		return error;
61 
62 	return git_refdb_reflog_read(reflog, refdb, name);
63 }
64 
git_reflog_write(git_reflog * reflog)65 int git_reflog_write(git_reflog *reflog)
66 {
67 	git_refdb *db;
68 
69 	assert(reflog && reflog->db);
70 
71 	db = reflog->db;
72 	return db->backend->reflog_write(db->backend, reflog);
73 }
74 
git_reflog_append(git_reflog * reflog,const git_oid * new_oid,const git_signature * committer,const char * msg)75 int git_reflog_append(git_reflog *reflog, const git_oid *new_oid, const git_signature *committer, const char *msg)
76 {
77 	git_reflog_entry *entry;
78 	const git_reflog_entry *previous;
79 	const char *newline;
80 
81 	assert(reflog && new_oid && committer);
82 
83 	entry = git__calloc(1, sizeof(git_reflog_entry));
84 	GIT_ERROR_CHECK_ALLOC(entry);
85 
86 	if ((git_signature_dup(&entry->committer, committer)) < 0)
87 		goto cleanup;
88 
89 	if (msg != NULL) {
90 		if ((entry->msg = git__strdup(msg)) == NULL)
91 			goto cleanup;
92 
93 		newline = strchr(msg, '\n');
94 
95 		if (newline) {
96 			if (newline[1] != '\0') {
97 				git_error_set(GIT_ERROR_INVALID, "reflog message cannot contain newline");
98 				goto cleanup;
99 			}
100 
101 			entry->msg[newline - msg] = '\0';
102 		}
103 	}
104 
105 	previous = git_reflog_entry_byindex(reflog, 0);
106 
107 	if (previous == NULL)
108 		git_oid_fromstr(&entry->oid_old, GIT_OID_HEX_ZERO);
109 	else
110 		git_oid_cpy(&entry->oid_old, &previous->oid_cur);
111 
112 	git_oid_cpy(&entry->oid_cur, new_oid);
113 
114 	if (git_vector_insert(&reflog->entries, entry) < 0)
115 		goto cleanup;
116 
117 	return 0;
118 
119 cleanup:
120 	git_reflog_entry__free(entry);
121 	return -1;
122 }
123 
git_reflog_rename(git_repository * repo,const char * old_name,const char * new_name)124 int git_reflog_rename(git_repository *repo, const char *old_name, const char *new_name)
125 {
126 	git_refdb *refdb;
127 	int error;
128 
129 	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
130 		return -1;
131 
132 	return refdb->backend->reflog_rename(refdb->backend, old_name, new_name);
133 }
134 
git_reflog_delete(git_repository * repo,const char * name)135 int git_reflog_delete(git_repository *repo, const char *name)
136 {
137 	git_refdb *refdb;
138 	int error;
139 
140 	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
141 		return -1;
142 
143 	return refdb->backend->reflog_delete(refdb->backend, name);
144 }
145 
git_reflog_entrycount(git_reflog * reflog)146 size_t git_reflog_entrycount(git_reflog *reflog)
147 {
148 	assert(reflog);
149 	return reflog->entries.length;
150 }
151 
git_reflog_entry_byindex(const git_reflog * reflog,size_t idx)152 const git_reflog_entry * git_reflog_entry_byindex(const git_reflog *reflog, size_t idx)
153 {
154 	assert(reflog);
155 
156 	if (idx >= reflog->entries.length)
157 		return NULL;
158 
159 	return git_vector_get(
160 		&reflog->entries, reflog_inverse_index(idx, reflog->entries.length));
161 }
162 
git_reflog_entry_id_old(const git_reflog_entry * entry)163 const git_oid * git_reflog_entry_id_old(const git_reflog_entry *entry)
164 {
165 	assert(entry);
166 	return &entry->oid_old;
167 }
168 
git_reflog_entry_id_new(const git_reflog_entry * entry)169 const git_oid * git_reflog_entry_id_new(const git_reflog_entry *entry)
170 {
171 	assert(entry);
172 	return &entry->oid_cur;
173 }
174 
git_reflog_entry_committer(const git_reflog_entry * entry)175 const git_signature * git_reflog_entry_committer(const git_reflog_entry *entry)
176 {
177 	assert(entry);
178 	return entry->committer;
179 }
180 
git_reflog_entry_message(const git_reflog_entry * entry)181 const char * git_reflog_entry_message(const git_reflog_entry *entry)
182 {
183 	assert(entry);
184 	return entry->msg;
185 }
186 
git_reflog_drop(git_reflog * reflog,size_t idx,int rewrite_previous_entry)187 int git_reflog_drop(git_reflog *reflog, size_t idx, int rewrite_previous_entry)
188 {
189 	size_t entrycount;
190 	git_reflog_entry *entry, *previous;
191 
192 	entrycount = git_reflog_entrycount(reflog);
193 
194 	entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
195 
196 	if (entry == NULL) {
197 		git_error_set(GIT_ERROR_REFERENCE, "no reflog entry at index %"PRIuZ, idx);
198 		return GIT_ENOTFOUND;
199 	}
200 
201 	git_reflog_entry__free(entry);
202 
203 	if (git_vector_remove(
204 			&reflog->entries, reflog_inverse_index(idx, entrycount)) < 0)
205 		return -1;
206 
207 	if (!rewrite_previous_entry)
208 		return 0;
209 
210 	/* No need to rewrite anything when removing the most recent entry */
211 	if (idx == 0)
212 		return 0;
213 
214 	/* Have the latest entry just been dropped? */
215 	if (entrycount == 1)
216 		return 0;
217 
218 	entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx - 1);
219 
220 	/* If the oldest entry has just been removed... */
221 	if (idx == entrycount - 1) {
222 		/* ...clear the oid_old member of the "new" oldest entry */
223 		if (git_oid_fromstr(&entry->oid_old, GIT_OID_HEX_ZERO) < 0)
224 			return -1;
225 
226 		return 0;
227 	}
228 
229 	previous = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
230 	git_oid_cpy(&entry->oid_old, &previous->oid_cur);
231 
232 	return 0;
233 }
234