1 /******************************************************************************
3 * Copyright (C) 2010 David Barr <david.barr@cordelta.com>.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice(s), this list of conditions and the following disclaimer
11 * unmodified other than the allowable addition of one or more
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice(s), this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 ******************************************************************************/
38 #include "string_pool.h"
39 #include "repo_tree.h"
42 typedef struct repo_dirent_s repo_dirent_t
;
44 struct repo_dirent_s
{
47 uint32_t content_offset
;
50 typedef struct repo_dir_s repo_dir_t
;
54 uint32_t first_offset
;
57 typedef struct repo_commit_s repo_commit_t
;
59 struct repo_commit_s
{
61 uint32_t root_dir_offset
;
64 obj_pool_gen(commit
, repo_commit_t
, 4096);
65 obj_pool_gen(dir
, repo_dir_t
, 4096);
66 obj_pool_gen(dirent
, repo_dirent_t
, 4096);
68 static uint32_t num_dirs_saved
= 0;
69 static uint32_t num_dirents_saved
= 0;
70 static uint32_t active_commit
= -1;
72 static repo_dir_t
*repo_commit_root_dir(repo_commit_t
* commit
)
74 return dir_pointer(commit
->root_dir_offset
);
77 static repo_dirent_t
*repo_first_dirent(repo_dir_t
* dir
)
79 return dirent_pointer(dir
->first_offset
);
82 static int repo_dirent_name_cmp(const void *a
, const void *b
)
84 return (((repo_dirent_t
*) a
)->name_offset
85 > ((repo_dirent_t
*) b
)->name_offset
) -
86 (((repo_dirent_t
*) a
)->name_offset
87 < ((repo_dirent_t
*) b
)->name_offset
);
90 static repo_dirent_t
*repo_dirent_by_name(repo_dir_t
* dir
,
94 if (dir
== NULL
|| dir
->size
== 0)
96 key
.name_offset
= name_offset
;
97 return bsearch(&key
, repo_first_dirent(dir
), dir
->size
,
98 sizeof(repo_dirent_t
), repo_dirent_name_cmp
);
101 static int repo_dirent_is_dir(repo_dirent_t
* dirent
)
103 return dirent
!= NULL
&& dirent
->mode
== REPO_MODE_DIR
;
106 static int repo_dirent_is_blob(repo_dirent_t
* dirent
)
108 return dirent
->mode
== REPO_MODE_BLB
|| dirent
->mode
== REPO_MODE_EXE
;
111 static int repo_dirent_is_executable(repo_dirent_t
* dirent
)
113 return dirent
->mode
== REPO_MODE_EXE
;
116 static int repo_dirent_is_symlink(repo_dirent_t
* dirent
)
118 return dirent
->mode
== REPO_MODE_LNK
;
121 static repo_dir_t
*repo_dir_from_dirent(repo_dirent_t
* dirent
)
123 if (!repo_dirent_is_dir(dirent
))
125 return dir_pointer(dirent
->content_offset
);
128 static uint32_t dir_with_dirents_alloc(uint32_t size
)
130 uint32_t offset
= dir_alloc(1);
131 dir_pointer(offset
)->size
= size
;
132 dir_pointer(offset
)->first_offset
= dirent_alloc(size
);
136 static repo_dir_t
*repo_clone_dir(repo_dir_t
* orig_dir
, uint32_t padding
)
138 uint32_t orig_o
, new_o
, dirent_o
;
139 orig_o
= dir_offset(orig_dir
);
140 if (orig_o
< num_dirs_saved
) {
141 new_o
= dir_with_dirents_alloc(orig_dir
->size
+ padding
);
142 orig_dir
= dir_pointer(orig_o
);
143 dirent_o
= dir_pointer(new_o
)->first_offset
;
148 dirent_o
= dirent_alloc(orig_dir
->size
+ padding
);
150 memcpy(dirent_pointer(dirent_o
), repo_first_dirent(orig_dir
),
151 orig_dir
->size
* sizeof(repo_dirent_t
));
152 dir_pointer(new_o
)->size
= orig_dir
->size
+ padding
;
153 dir_pointer(new_o
)->first_offset
= dirent_o
;
154 return dir_pointer(new_o
);
157 static char repo_path_buffer
[REPO_MAX_PATH_LEN
];
158 static repo_dirent_t
*repo_read_dirent(uint32_t revision
, char *path
)
162 repo_dir_t
*dir
= NULL
;
163 repo_dirent_t
*dirent
= NULL
;
164 dir
= repo_commit_root_dir(commit_pointer(revision
));
165 strncpy(repo_path_buffer
, path
, REPO_MAX_PATH_LEN
);
166 repo_path_buffer
[REPO_MAX_PATH_LEN
- 1] = '\0';
167 path
= repo_path_buffer
;
168 for (name
= pool_tok_r(path
, "/", &ctx
);
169 ~name
; name
= pool_tok_r(NULL
, "/", &ctx
)) {
170 dirent
= repo_dirent_by_name(dir
, name
);
171 if (dirent
== NULL
) {
173 } else if (repo_dirent_is_dir(dirent
)) {
174 dir
= repo_dir_from_dirent(dirent
);
183 repo_write_dirent(char *path
, uint32_t mode
, uint32_t content_offset
,
187 uint32_t name
, revision
, dirent_o
, dir_o
, parent_dir_o
;
189 repo_dirent_t
*dirent
= NULL
;
190 revision
= active_commit
;
191 dir
= repo_commit_root_dir(commit_pointer(revision
));
192 dir
= repo_clone_dir(dir
, 0);
193 commit_pointer(revision
)->root_dir_offset
= dir_offset(dir
);
194 strncpy(repo_path_buffer
, path
, REPO_MAX_PATH_LEN
);
195 repo_path_buffer
[REPO_MAX_PATH_LEN
- 1] = '\0';
196 path
= repo_path_buffer
;
197 for (name
= pool_tok_r(path
, "/", &ctx
); ~name
;
198 name
= pool_tok_r(NULL
, "/", &ctx
)) {
199 parent_dir_o
= dir_offset(dir
);
200 dirent
= repo_dirent_by_name(dir
, name
);
201 if (dirent
== NULL
) {
202 dir
= repo_clone_dir(dir
, 1);
203 dirent
= &repo_first_dirent(dir
)[dir
->size
- 1];
204 dirent
->name_offset
= name
;
205 dirent
->mode
= REPO_MODE_DIR
;
206 qsort(repo_first_dirent(dir
), dir
->size
,
207 sizeof(repo_dirent_t
), repo_dirent_name_cmp
);
208 dirent
= repo_dirent_by_name(dir
, name
);
209 dir_o
= dir_with_dirents_alloc(0);
210 dirent
->content_offset
= dir_o
;
211 dir
= dir_pointer(dir_o
);
212 } else if (dir
= repo_dir_from_dirent(dirent
)) {
213 dirent_o
= dirent_offset(dirent
);
214 dir
= repo_clone_dir(dir
, 0);
216 dirent_pointer(dirent_o
)->content_offset
= dir_offset(dir
);
218 dirent
->mode
= REPO_MODE_DIR
;
219 dirent_o
= dirent_offset(dirent
);
220 dir_o
= dir_with_dirents_alloc(0);
221 dirent
= dirent_pointer(dirent_o
);
222 dir
= dir_pointer(dir_o
);
223 dirent
->content_offset
= dir_o
;
228 dirent
->content_offset
= content_offset
;
229 if (del
&& ~parent_dir_o
) {
230 dirent
->name_offset
= ~0;
231 dir
= dir_pointer(parent_dir_o
);
232 qsort(repo_first_dirent(dir
), dir
->size
,
233 sizeof(repo_dirent_t
), repo_dirent_name_cmp
);
239 uint32_t repo_copy(uint32_t revision
, char *src
, char *dst
)
241 uint32_t mode
= 0, content_offset
= 0;
242 repo_dirent_t
*src_dirent
;
243 fprintf(stderr
, "C %d:%s %s\n", revision
, src
, dst
);
244 src_dirent
= repo_read_dirent(revision
, src
);
245 if (src_dirent
!= NULL
) {
246 mode
= src_dirent
->mode
;
247 content_offset
= src_dirent
->content_offset
;
248 repo_write_dirent(dst
, mode
, content_offset
, 0);
253 void repo_add(char *path
, uint32_t mode
, uint32_t blob_mark
)
255 fprintf(stderr
, "A %s %d\n", path
, blob_mark
);
256 repo_write_dirent(path
, mode
, blob_mark
, 0);
259 uint32_t repo_replace(char *path
, uint32_t blob_mark
)
262 repo_dirent_t
*src_dirent
;
263 src_dirent
= repo_read_dirent(active_commit
, path
);
264 if (src_dirent
!= NULL
) {
265 mode
= src_dirent
->mode
;
266 fprintf(stderr
, "R %s %06o %d\n", path
, mode
, blob_mark
);
267 repo_write_dirent(path
, mode
, blob_mark
, 0);
272 void repo_modify(char *path
, uint32_t mode
, uint32_t blob_mark
)
274 fprintf(stderr
, "M %s %d\n", path
, blob_mark
);
275 repo_write_dirent(path
, mode
, blob_mark
, 0);
278 void repo_delete(char *path
)
280 fprintf(stderr
, "D %s\n", path
);
281 repo_write_dirent(path
, 0, 0, 1);
284 static void repo_print_path(uint32_t depth
, uint32_t * path
)
286 pool_print_seq(depth
, path
, '/', stdout
);
289 static void repo_git_delete(uint32_t depth
, uint32_t * path
)
293 repo_print_path(depth
, path
);
298 repo_git_add_r(uint32_t depth
, uint32_t * path
, repo_dir_t
* dir
);
301 repo_git_add(uint32_t depth
, uint32_t * path
, repo_dirent_t
* dirent
)
303 if (repo_dirent_is_dir(dirent
)) {
304 repo_git_add_r(depth
, path
, repo_dir_from_dirent(dirent
));
306 printf("M %06o :%d ", dirent
->mode
, dirent
->content_offset
);
307 repo_print_path(depth
, path
);
313 repo_git_add_r(uint32_t depth
, uint32_t * path
, repo_dir_t
* dir
)
317 de
= repo_first_dirent(dir
);
318 for (o
= 0; o
< dir
->size
; o
++) {
319 path
[depth
] = de
[o
].name_offset
;
320 repo_git_add(depth
+ 1, path
, &de
[o
]);
325 repo_diff_r(uint32_t depth
, uint32_t * path
, repo_dir_t
* dir1
,
329 repo_dirent_t
*de1
, *de2
;
330 de1
= repo_first_dirent(dir1
);
331 de2
= repo_first_dirent(dir2
);
332 for (o1
= o2
= 0; o1
< dir1
->size
&& o2
< dir2
->size
;) {
333 if (de1
[o1
].name_offset
< de2
[o2
].name_offset
) {
335 path
[depth
] = de1
[o1
].name_offset
;
336 repo_git_delete(depth
+ 1, path
);
338 } else if (de1
[o1
].name_offset
== de2
[o2
].name_offset
) {
339 path
[depth
] = de1
[o1
].name_offset
;
340 if (de1
[o1
].content_offset
!= de2
[o2
].content_offset
) {
341 if (repo_dirent_is_dir(&de1
[o1
])
342 && repo_dirent_is_dir(&de2
[o2
])) {
344 repo_diff_r(depth
+ 1, path
,
345 repo_dir_from_dirent(&de1
[o1
]),
346 repo_dir_from_dirent(&de2
[o2
]));
348 /* delete o1, add o2 */
349 if (repo_dirent_is_dir(&de1
[o1
]) !=
350 repo_dirent_is_dir(&de2
[o2
])) {
351 repo_git_delete(depth
+ 1, path
);
353 repo_git_add(depth
+ 1, path
, &de2
[o2
]);
360 path
[depth
] = de2
[o2
].name_offset
;
361 repo_git_add(depth
+ 1, path
, &de2
[o2
]);
365 for (; o1
< dir1
->size
; o1
++) {
367 path
[depth
] = de1
[o1
].name_offset
;
368 repo_git_delete(depth
+ 1, path
);
370 for (; o2
< dir2
->size
; o2
++) {
372 path
[depth
] = de2
[o2
].name_offset
;
373 repo_git_add(depth
+ 1, path
, &de2
[o2
]);
377 static uint32_t path_stack
[1000];
378 static void repo_diff(uint32_t r1
, uint32_t r2
)
382 repo_commit_root_dir(commit_pointer(r1
)),
383 repo_commit_root_dir(commit_pointer(r2
)));
386 static char gitsvnline
[4096];
388 static void repo_git_commit(uint32_t revision
, char * author
, char * log
,
389 char * uuid
, char * url
, time_t timestamp
)
391 printf("commit refs/heads/master\nmark :%d\n", revision
);
392 printf("committer %s <%s@%s> %ld +0000\n",
393 author
, author
, uuid
? uuid
: "local", timestamp
);
395 snprintf(gitsvnline
, 4096, "\n\ngit-svn-id: %s@%d %s\n",
396 url
, revision
, uuid
);
400 printf("data %ld\n%s%s\n",
401 strlen(log
) + strlen(gitsvnline
), log
, gitsvnline
);
402 repo_diff(revision
- 1, revision
);
405 printf("progress Imported commit %d.\n\n", revision
);
408 void repo_commit(uint32_t revision
, char * author
, char * log
, char * uuid
,
409 char * url
, time_t timestamp
)
411 fprintf(stderr
, "R %d\n", revision
);
413 active_commit
= commit_alloc(1);
414 commit_pointer(active_commit
)->root_dir_offset
=
415 dir_with_dirents_alloc(0);
417 repo_git_commit(revision
, author
, log
, uuid
, url
, timestamp
);
419 num_dirs_saved
= dir_pool
.size
;
420 num_dirents_saved
= dirent_pool
.size
;
421 active_commit
= commit_alloc(1);
422 commit_pointer(active_commit
)->root_dir_offset
=
423 commit_pointer(active_commit
- 1)->root_dir_offset
;
426 void repo_copy_blob(uint32_t mode
, uint32_t mark
, uint32_t len
)
428 if (mode
== REPO_MODE_LNK
) {
429 /* svn symlink blobs start with "link " */
430 buffer_skip_bytes(5);
433 printf("blob\nmark :%d\ndata %d\n", mark
, len
);
434 buffer_copy_bytes(len
);