2 * Licensed under a two-clause BSD-style license.
3 * See LICENSE for details.
6 #include "git-compat-util.h"
8 #include "string_pool.h"
11 #include "fast_export.h"
17 struct trp_node children
;
19 uint32_t content_offset
;
23 struct trp_root entries
;
27 uint32_t root_dir_offset
;
30 /* Memory pools for commit, dir and dirent */
31 obj_pool_gen(commit
, struct repo_commit
, 4096)
32 obj_pool_gen(dir
, struct repo_dir
, 4096)
33 obj_pool_gen(dent
, struct repo_dirent
, 4096)
35 static uint32_t active_commit
;
38 static int repo_dirent_name_cmp(const void *a
, const void *b
);
40 /* Treap for directory entries */
41 trp_gen(static, dent_
, struct repo_dirent
, children
, dent
, repo_dirent_name_cmp
);
43 uint32_t next_blob_mark(void)
48 static struct repo_dir
*repo_commit_root_dir(struct repo_commit
*commit
)
50 return dir_pointer(commit
->root_dir_offset
);
53 static struct repo_dirent
*repo_first_dirent(struct repo_dir
*dir
)
55 return dent_first(&dir
->entries
);
58 static int repo_dirent_name_cmp(const void *a
, const void *b
)
60 const struct repo_dirent
*dent1
= a
, *dent2
= b
;
61 uint32_t a_offset
= dent1
->name_offset
;
62 uint32_t b_offset
= dent2
->name_offset
;
63 return (a_offset
> b_offset
) - (a_offset
< b_offset
);
66 static int repo_dirent_is_dir(struct repo_dirent
*dent
)
68 return dent
!= NULL
&& dent
->mode
== REPO_MODE_DIR
;
71 static struct repo_dir
*repo_dir_from_dirent(struct repo_dirent
*dent
)
73 if (!repo_dirent_is_dir(dent
))
75 return dir_pointer(dent
->content_offset
);
78 static struct repo_dir
*repo_clone_dir(struct repo_dir
*orig_dir
)
80 uint32_t orig_o
, new_o
;
81 orig_o
= dir_offset(orig_dir
);
82 if (orig_o
>= dir_pool
.committed
)
85 orig_dir
= dir_pointer(orig_o
);
86 *dir_pointer(new_o
) = *orig_dir
;
87 return dir_pointer(new_o
);
90 static struct repo_dirent
*repo_read_dirent(uint32_t revision
, uint32_t *path
)
93 struct repo_dirent
*key
= dent_pointer(dent_alloc(1));
94 struct repo_dir
*dir
= NULL
;
95 struct repo_dirent
*dent
= NULL
;
96 dir
= repo_commit_root_dir(commit_pointer(revision
));
97 while (~(name
= *path
++)) {
98 key
->name_offset
= name
;
99 dent
= dent_search(&dir
->entries
, key
);
100 if (dent
== NULL
|| !repo_dirent_is_dir(dent
))
102 dir
= repo_dir_from_dirent(dent
);
108 static void repo_write_dirent(uint32_t *path
, uint32_t mode
,
109 uint32_t content_offset
, uint32_t del
)
111 uint32_t name
, revision
, dir_o
= ~0, parent_dir_o
= ~0;
112 struct repo_dir
*dir
;
113 struct repo_dirent
*key
;
114 struct repo_dirent
*dent
= NULL
;
115 revision
= active_commit
;
116 dir
= repo_commit_root_dir(commit_pointer(revision
));
117 dir
= repo_clone_dir(dir
);
118 commit_pointer(revision
)->root_dir_offset
= dir_offset(dir
);
119 while (~(name
= *path
++)) {
120 parent_dir_o
= dir_offset(dir
);
122 key
= dent_pointer(dent_alloc(1));
123 key
->name_offset
= name
;
125 dent
= dent_search(&dir
->entries
, key
);
132 dent
->mode
= REPO_MODE_DIR
;
133 dent
->content_offset
= 0;
134 dent
= dent_insert(&dir
->entries
, dent
);
137 if (dent_offset(dent
) < dent_pool
.committed
) {
138 dir_o
= repo_dirent_is_dir(dent
) ?
139 dent
->content_offset
: ~0;
140 dent_remove(&dir
->entries
, dent
);
141 dent
= dent_pointer(dent_alloc(1));
142 dent
->name_offset
= name
;
143 dent
->mode
= REPO_MODE_DIR
;
144 dent
->content_offset
= dir_o
;
145 dent
= dent_insert(&dir
->entries
, dent
);
148 dir
= repo_dir_from_dirent(dent
);
149 dir
= repo_clone_dir(dir
);
150 dent
->content_offset
= dir_offset(dir
);
155 dent
->content_offset
= content_offset
;
156 if (del
&& ~parent_dir_o
)
157 dent_remove(&dir_pointer(parent_dir_o
)->entries
, dent
);
160 uint32_t repo_copy(uint32_t revision
, uint32_t *src
, uint32_t *dst
)
162 uint32_t mode
= 0, content_offset
= 0;
163 struct repo_dirent
*src_dent
;
164 src_dent
= repo_read_dirent(revision
, src
);
165 if (src_dent
!= NULL
) {
166 mode
= src_dent
->mode
;
167 content_offset
= src_dent
->content_offset
;
168 repo_write_dirent(dst
, mode
, content_offset
, 0);
173 void repo_add(uint32_t *path
, uint32_t mode
, uint32_t blob_mark
)
175 repo_write_dirent(path
, mode
, blob_mark
, 0);
178 uint32_t repo_replace(uint32_t *path
, uint32_t blob_mark
)
181 struct repo_dirent
*src_dent
;
182 src_dent
= repo_read_dirent(active_commit
, path
);
183 if (src_dent
!= NULL
) {
184 mode
= src_dent
->mode
;
185 repo_write_dirent(path
, mode
, blob_mark
, 0);
190 void repo_modify(uint32_t *path
, uint32_t mode
, uint32_t blob_mark
)
192 struct repo_dirent
*src_dent
;
193 src_dent
= repo_read_dirent(active_commit
, path
);
194 if (src_dent
!= NULL
&& blob_mark
== 0)
195 blob_mark
= src_dent
->content_offset
;
196 repo_write_dirent(path
, mode
, blob_mark
, 0);
199 void repo_delete(uint32_t *path
)
201 repo_write_dirent(path
, 0, 0, 1);
204 static void repo_git_add_r(uint32_t depth
, uint32_t *path
, struct repo_dir
*dir
);
206 static void repo_git_add(uint32_t depth
, uint32_t *path
, struct repo_dirent
*dent
)
208 if (repo_dirent_is_dir(dent
))
209 repo_git_add_r(depth
, path
, repo_dir_from_dirent(dent
));
211 fast_export_modify(depth
, path
,
212 dent
->mode
, dent
->content_offset
);
215 static void repo_git_add_r(uint32_t depth
, uint32_t *path
, struct repo_dir
*dir
)
217 struct repo_dirent
*de
= repo_first_dirent(dir
);
219 path
[depth
] = de
->name_offset
;
220 repo_git_add(depth
+ 1, path
, de
);
221 de
= dent_next(&dir
->entries
, de
);
225 static void repo_diff_r(uint32_t depth
, uint32_t *path
, struct repo_dir
*dir1
,
226 struct repo_dir
*dir2
)
228 struct repo_dirent
*de1
, *de2
;
229 de1
= repo_first_dirent(dir1
);
230 de2
= repo_first_dirent(dir2
);
233 if (de1
->name_offset
< de2
->name_offset
) {
234 path
[depth
] = de1
->name_offset
;
235 fast_export_delete(depth
+ 1, path
);
236 de1
= dent_next(&dir1
->entries
, de1
);
239 if (de1
->name_offset
> de2
->name_offset
) {
240 path
[depth
] = de2
->name_offset
;
241 repo_git_add(depth
+ 1, path
, de2
);
242 de2
= dent_next(&dir2
->entries
, de2
);
245 path
[depth
] = de1
->name_offset
;
247 if (de1
->mode
== de2
->mode
&&
248 de1
->content_offset
== de2
->content_offset
) {
250 } else if (repo_dirent_is_dir(de1
) && repo_dirent_is_dir(de2
)) {
251 repo_diff_r(depth
+ 1, path
,
252 repo_dir_from_dirent(de1
),
253 repo_dir_from_dirent(de2
));
254 } else if (!repo_dirent_is_dir(de1
) && !repo_dirent_is_dir(de2
)) {
255 repo_git_add(depth
+ 1, path
, de2
);
257 fast_export_delete(depth
+ 1, path
);
258 repo_git_add(depth
+ 1, path
, de2
);
260 de1
= dent_next(&dir1
->entries
, de1
);
261 de2
= dent_next(&dir2
->entries
, de2
);
264 path
[depth
] = de1
->name_offset
;
265 fast_export_delete(depth
+ 1, path
);
266 de1
= dent_next(&dir1
->entries
, de1
);
269 path
[depth
] = de2
->name_offset
;
270 repo_git_add(depth
+ 1, path
, de2
);
271 de2
= dent_next(&dir2
->entries
, de2
);
275 static uint32_t path_stack
[REPO_MAX_PATH_DEPTH
];
277 void repo_diff(uint32_t r1
, uint32_t r2
)
281 repo_commit_root_dir(commit_pointer(r1
)),
282 repo_commit_root_dir(commit_pointer(r2
)));
285 void repo_commit(uint32_t revision
, uint32_t author
, char *log
, uint32_t uuid
,
286 uint32_t url
, unsigned long timestamp
)
288 fast_export_commit(revision
, author
, log
, uuid
, url
, timestamp
);
291 active_commit
= commit_alloc(1);
292 commit_pointer(active_commit
)->root_dir_offset
=
293 commit_pointer(active_commit
- 1)->root_dir_offset
;
296 static void mark_init(void)
300 for (i
= 0; i
< dent_pool
.size
; i
++)
301 if (!repo_dirent_is_dir(dent_pointer(i
)) &&
302 dent_pointer(i
)->content_offset
> mark
)
303 mark
= dent_pointer(i
)->content_offset
;
310 if (commit_pool
.size
== 0) {
311 /* Create empty tree for commit 0. */
313 commit_pointer(0)->root_dir_offset
= dir_alloc(1);
314 dir_pointer(0)->entries
.trp_root
= ~0;
317 /* Preallocate next commit, ready for changes. */
318 active_commit
= commit_alloc(1);
319 commit_pointer(active_commit
)->root_dir_offset
=
320 commit_pointer(active_commit
- 1)->root_dir_offset
;
323 void repo_reset(void)