3 #include "string_pool.h"
6 #include "fast_export.h"
8 typedef struct repo_dirent_s repo_dirent_t
;
10 struct repo_dirent_s
{
13 uint32_t content_offset
;
16 typedef struct repo_dir_s repo_dir_t
;
20 uint32_t first_offset
;
23 typedef struct repo_commit_s repo_commit_t
;
25 struct repo_commit_s
{
27 uint32_t root_dir_offset
;
30 obj_pool_gen(commit
, repo_commit_t
, 4096);
31 obj_pool_gen(dir
, repo_dir_t
, 4096);
32 obj_pool_gen(dirent
, repo_dirent_t
, 4096);
34 static uint32_t num_dirs_saved
= 0;
35 static uint32_t num_dirents_saved
= 0;
36 static uint32_t active_commit
= -1;
38 static repo_dir_t
*repo_commit_root_dir(repo_commit_t
* commit
)
40 return dir_pointer(commit
->root_dir_offset
);
43 static repo_dirent_t
*repo_first_dirent(repo_dir_t
* dir
)
45 return dirent_pointer(dir
->first_offset
);
48 static int repo_dirent_name_cmp(const void *a
, const void *b
)
50 return (((repo_dirent_t
*) a
)->name_offset
51 > ((repo_dirent_t
*) b
)->name_offset
) -
52 (((repo_dirent_t
*) a
)->name_offset
53 < ((repo_dirent_t
*) b
)->name_offset
);
56 static repo_dirent_t
*repo_dirent_by_name(repo_dir_t
* dir
,
60 if (dir
== NULL
|| dir
->size
== 0)
62 key
.name_offset
= name_offset
;
63 return bsearch(&key
, repo_first_dirent(dir
), dir
->size
,
64 sizeof(repo_dirent_t
), repo_dirent_name_cmp
);
67 static int repo_dirent_is_dir(repo_dirent_t
* dirent
)
69 return dirent
!= NULL
&& dirent
->mode
== REPO_MODE_DIR
;
72 static repo_dir_t
*repo_dir_from_dirent(repo_dirent_t
* dirent
)
74 if (!repo_dirent_is_dir(dirent
))
76 return dir_pointer(dirent
->content_offset
);
79 static uint32_t dir_with_dirents_alloc(uint32_t size
)
81 uint32_t offset
= dir_alloc(1);
82 dir_pointer(offset
)->size
= size
;
83 dir_pointer(offset
)->first_offset
= dirent_alloc(size
);
87 static repo_dir_t
*repo_clone_dir(repo_dir_t
* orig_dir
, uint32_t padding
)
89 uint32_t orig_o
, new_o
, dirent_o
;
90 orig_o
= dir_offset(orig_dir
);
91 if (orig_o
< num_dirs_saved
) {
92 new_o
= dir_with_dirents_alloc(orig_dir
->size
+ padding
);
93 orig_dir
= dir_pointer(orig_o
);
94 dirent_o
= dir_pointer(new_o
)->first_offset
;
99 dirent_o
= dirent_alloc(orig_dir
->size
+ padding
);
101 memcpy(dirent_pointer(dirent_o
), repo_first_dirent(orig_dir
),
102 orig_dir
->size
* sizeof(repo_dirent_t
));
103 dir_pointer(new_o
)->size
= orig_dir
->size
+ padding
;
104 dir_pointer(new_o
)->first_offset
= dirent_o
;
105 return dir_pointer(new_o
);
108 static char repo_path_buffer
[REPO_MAX_PATH_LEN
];
109 static repo_dirent_t
*repo_read_dirent(uint32_t revision
, char *path
)
113 repo_dir_t
*dir
= NULL
;
114 repo_dirent_t
*dirent
= NULL
;
115 dir
= repo_commit_root_dir(commit_pointer(revision
));
116 strncpy(repo_path_buffer
, path
, REPO_MAX_PATH_LEN
);
117 repo_path_buffer
[REPO_MAX_PATH_LEN
- 1] = '\0';
118 path
= repo_path_buffer
;
119 for (name
= pool_tok_r(path
, "/", &ctx
);
120 ~name
; name
= pool_tok_r(NULL
, "/", &ctx
)) {
121 dirent
= repo_dirent_by_name(dir
, name
);
122 if (dirent
== NULL
) {
124 } else if (repo_dirent_is_dir(dirent
)) {
125 dir
= repo_dir_from_dirent(dirent
);
134 repo_write_dirent(char *path
, uint32_t mode
, uint32_t content_offset
,
138 uint32_t name
, revision
, dirent_o
, dir_o
, parent_dir_o
;
140 repo_dirent_t
*dirent
= NULL
;
141 revision
= active_commit
;
142 dir
= repo_commit_root_dir(commit_pointer(revision
));
143 dir
= repo_clone_dir(dir
, 0);
144 commit_pointer(revision
)->root_dir_offset
= dir_offset(dir
);
145 strncpy(repo_path_buffer
, path
, REPO_MAX_PATH_LEN
);
146 repo_path_buffer
[REPO_MAX_PATH_LEN
- 1] = '\0';
147 path
= repo_path_buffer
;
148 for (name
= pool_tok_r(path
, "/", &ctx
); ~name
;
149 name
= pool_tok_r(NULL
, "/", &ctx
)) {
150 parent_dir_o
= dir_offset(dir
);
151 dirent
= repo_dirent_by_name(dir
, name
);
152 if (dirent
== NULL
) {
153 dir
= repo_clone_dir(dir
, 1);
154 dirent
= &repo_first_dirent(dir
)[dir
->size
- 1];
155 dirent
->name_offset
= name
;
156 dirent
->mode
= REPO_MODE_DIR
;
157 qsort(repo_first_dirent(dir
), dir
->size
,
158 sizeof(repo_dirent_t
), repo_dirent_name_cmp
);
159 dirent
= repo_dirent_by_name(dir
, name
);
160 dir_o
= dir_with_dirents_alloc(0);
161 dirent
->content_offset
= dir_o
;
162 dir
= dir_pointer(dir_o
);
163 } else if ((dir
= repo_dir_from_dirent(dirent
))) {
164 dirent_o
= dirent_offset(dirent
);
165 dir
= repo_clone_dir(dir
, 0);
167 dirent_pointer(dirent_o
)->content_offset
= dir_offset(dir
);
169 dirent
->mode
= REPO_MODE_DIR
;
170 dirent_o
= dirent_offset(dirent
);
171 dir_o
= dir_with_dirents_alloc(0);
172 dirent
= dirent_pointer(dirent_o
);
173 dir
= dir_pointer(dir_o
);
174 dirent
->content_offset
= dir_o
;
179 dirent
->content_offset
= content_offset
;
180 if (del
&& ~parent_dir_o
) {
181 dirent
->name_offset
= ~0;
182 dir
= dir_pointer(parent_dir_o
);
183 qsort(repo_first_dirent(dir
), dir
->size
,
184 sizeof(repo_dirent_t
), repo_dirent_name_cmp
);
190 uint32_t repo_copy(uint32_t revision
, char *src
, char *dst
)
192 uint32_t mode
= 0, content_offset
= 0;
193 repo_dirent_t
*src_dirent
;
194 src_dirent
= repo_read_dirent(revision
, src
);
195 if (src_dirent
!= NULL
) {
196 mode
= src_dirent
->mode
;
197 content_offset
= src_dirent
->content_offset
;
198 repo_write_dirent(dst
, mode
, content_offset
, 0);
203 void repo_add(char *path
, uint32_t mode
, uint32_t blob_mark
)
205 repo_write_dirent(path
, mode
, blob_mark
, 0);
208 uint32_t repo_replace(char *path
, uint32_t blob_mark
)
211 repo_dirent_t
*src_dirent
;
212 src_dirent
= repo_read_dirent(active_commit
, path
);
213 if (src_dirent
!= NULL
) {
214 mode
= src_dirent
->mode
;
215 repo_write_dirent(path
, mode
, blob_mark
, 0);
220 void repo_modify(char *path
, uint32_t mode
, uint32_t blob_mark
)
222 repo_write_dirent(path
, mode
, blob_mark
, 0);
225 void repo_delete(char *path
)
227 repo_write_dirent(path
, 0, 0, 1);
231 repo_git_add_r(uint32_t depth
, uint32_t * path
, repo_dir_t
* dir
);
234 repo_git_add(uint32_t depth
, uint32_t * path
, repo_dirent_t
* dirent
)
236 if (repo_dirent_is_dir(dirent
)) {
237 repo_git_add_r(depth
, path
, repo_dir_from_dirent(dirent
));
239 fast_export_modify(depth
, path
, dirent
->mode
, dirent
->content_offset
);
244 repo_git_add_r(uint32_t depth
, uint32_t * path
, repo_dir_t
* dir
)
248 de
= repo_first_dirent(dir
);
249 for (o
= 0; o
< dir
->size
; o
++) {
250 path
[depth
] = de
[o
].name_offset
;
251 repo_git_add(depth
+ 1, path
, &de
[o
]);
256 repo_diff_r(uint32_t depth
, uint32_t * path
, repo_dir_t
* dir1
,
259 repo_dirent_t
*de1
, *de2
, *max_de1
, *max_de2
;
260 de1
= repo_first_dirent(dir1
);
261 de2
= repo_first_dirent(dir2
);
262 max_de1
= &de1
[dir1
->size
];
263 max_de2
= &de2
[dir2
->size
];
265 while (de1
< max_de1
&& de2
< max_de2
) {
266 if (de1
->name_offset
< de2
->name_offset
) {
267 path
[depth
] = (de1
++)->name_offset
;
268 fast_export_delete(depth
+ 1, path
);
269 } else if (de1
->name_offset
== de2
->name_offset
) {
270 path
[depth
] = de1
->name_offset
;
271 if (de1
->content_offset
!= de2
->content_offset
) {
272 if (repo_dirent_is_dir(de1
) && repo_dirent_is_dir(de2
)) {
273 repo_diff_r(depth
+ 1, path
,
274 repo_dir_from_dirent(de1
),
275 repo_dir_from_dirent(de2
));
277 if (repo_dirent_is_dir(de1
) != repo_dirent_is_dir(de2
)) {
278 fast_export_delete(depth
+ 1, path
);
280 repo_git_add(depth
+ 1, path
, de2
);
286 path
[depth
] = de2
->name_offset
;
287 repo_git_add(depth
+ 1, path
, de2
++);
290 while (de1
< max_de1
) {
291 path
[depth
] = (de1
++)->name_offset
;
292 fast_export_delete(depth
+ 1, path
);
294 while (de2
< max_de2
) {
295 path
[depth
] = de2
->name_offset
;
296 repo_git_add(depth
+ 1, path
, de2
++);
300 static uint32_t path_stack
[1000];
301 void repo_diff(uint32_t r1
, uint32_t r2
)
305 repo_commit_root_dir(commit_pointer(r1
)),
306 repo_commit_root_dir(commit_pointer(r2
)));
309 void repo_commit(uint32_t revision
, char * author
, char * log
, char * uuid
,
310 char * url
, time_t timestamp
)
313 active_commit
= commit_alloc(1);
314 commit_pointer(active_commit
)->root_dir_offset
=
315 dir_with_dirents_alloc(0);
317 fast_export_commit(revision
, author
, log
, uuid
, url
, timestamp
);
319 num_dirs_saved
= dir_pool
.size
;
320 num_dirents_saved
= dirent_pool
.size
;
321 active_commit
= commit_alloc(1);
322 commit_pointer(active_commit
)->root_dir_offset
=
323 commit_pointer(active_commit
- 1)->root_dir_offset
;
326 void repo_reset(void)