2 * Parse and rearrange a svnadmin dump.
3 * Create the dump with:
4 * svnadmin dump --incremental -r<startrev>:<endrev> <repository> >outfile
13 #include "repo_tree.h"
14 #include "fast_export.h"
15 #include "line_buffer.h"
17 #include "string_pool.h"
19 #define NODEACT_REPLACE 4
20 #define NODEACT_DELETE 3
22 #define NODEACT_CHANGE 1
23 #define NODEACT_UNKNOWN 0
29 #define LENGTH_UNKNOWN (~0)
31 #define BLOB_MARK_OFFSET 1000000000
33 /* Create memory pool for log messages */
34 obj_pool_gen(log
, char, 4096);
36 static char* log_copy(uint32_t length
, char *log
)
39 log_free(log_pool
.size
);
40 buffer
= log_pointer(log_alloc(length
));
41 strncpy(buffer
, log
, length
);
46 uint32_t action
, propLength
, textLength
, srcRev
, srcMode
, mark
, type
;
47 uint32_t src
[REPO_MAX_PATH_DEPTH
], dst
[REPO_MAX_PATH_DEPTH
];
60 static void reset_node_ctx(char *fname
)
63 node_ctx
.action
= NODEACT_UNKNOWN
;
64 node_ctx
.propLength
= LENGTH_UNKNOWN
;
65 node_ctx
.textLength
= LENGTH_UNKNOWN
;
69 pool_tok_seq(REPO_MAX_PATH_DEPTH
, node_ctx
.dst
, "/", fname
);
73 static void reset_rev_ctx(uint32_t revision
)
75 rev_ctx
.revision
= revision
;
76 rev_ctx
.timestamp
= 0;
80 rev_ctx
.author
= NULL
;
83 static void reset_dump_ctx(char *url
)
93 static uint32_t next_blob_mark(void)
95 static uint32_t mark
= BLOB_MARK_OFFSET
;
99 static void read_props(void)
106 while ((t
= buffer_read_line()) && strcmp(t
, "PROPS-END")) {
107 if (!strncmp(t
, "K ", 2)) {
109 key
= pool_intern(buffer_read_string(len
));
111 } else if (!strncmp(t
, "V ", 2)) {
113 val
= buffer_read_string(len
);
114 if (key
== pool_intern("svn:log")) {
115 /* Value length excludes terminating nul. */
116 rev_ctx
.log
= log_copy(len
+ 1, val
);
117 } else if (key
== pool_intern("svn:author")) {
119 free(rev_ctx
.author
);
120 rev_ctx
.author
= strdup(val
);
121 } else if (key
== pool_intern("svn:date")) {
122 strptime(val
, "%FT%T", &tm
);
125 rev_ctx
.timestamp
= mktime(&tm
);
126 } else if (key
== pool_intern("svn:executable")) {
127 if (node_ctx
.type
== REPO_MODE_BLB
) {
128 node_ctx
.type
= REPO_MODE_EXE
;
130 } else if (key
== pool_intern("svn:special")) {
131 if (node_ctx
.type
== REPO_MODE_BLB
) {
132 node_ctx
.type
= REPO_MODE_LNK
;
141 static void handle_node(void)
143 if (node_ctx
.propLength
!= LENGTH_UNKNOWN
&& node_ctx
.propLength
) {
147 if (node_ctx
.srcRev
) {
149 repo_copy(node_ctx
.srcRev
, node_ctx
.src
, node_ctx
.dst
);
152 if (node_ctx
.textLength
!= LENGTH_UNKNOWN
&&
153 node_ctx
.type
!= REPO_MODE_DIR
) {
154 node_ctx
.mark
= next_blob_mark();
157 if (node_ctx
.action
== NODEACT_DELETE
) {
158 repo_delete(node_ctx
.dst
);
159 } else if (node_ctx
.action
== NODEACT_CHANGE
||
160 node_ctx
.action
== NODEACT_REPLACE
) {
161 if (node_ctx
.propLength
!= LENGTH_UNKNOWN
&&
162 node_ctx
.textLength
!= LENGTH_UNKNOWN
) {
163 repo_modify(node_ctx
.dst
, node_ctx
.type
, node_ctx
.mark
);
164 } else if (node_ctx
.textLength
!= LENGTH_UNKNOWN
) {
165 node_ctx
.srcMode
= repo_replace(node_ctx
.dst
, node_ctx
.mark
);
167 } else if (node_ctx
.action
== NODEACT_ADD
) {
168 if (node_ctx
.srcRev
&&
169 node_ctx
.propLength
== LENGTH_UNKNOWN
&&
170 node_ctx
.textLength
!= LENGTH_UNKNOWN
) {
171 node_ctx
.srcMode
= repo_replace(node_ctx
.dst
, node_ctx
.mark
);
172 } else if (node_ctx
.type
== REPO_MODE_DIR
||
173 node_ctx
.textLength
!= LENGTH_UNKNOWN
){
174 repo_add(node_ctx
.dst
, node_ctx
.type
, node_ctx
.mark
);
178 if (node_ctx
.propLength
== LENGTH_UNKNOWN
&& node_ctx
.srcMode
) {
179 node_ctx
.type
= node_ctx
.srcMode
;
183 fast_export_blob(node_ctx
.type
, node_ctx
.mark
, node_ctx
.textLength
);
184 } else if (node_ctx
.textLength
!= LENGTH_UNKNOWN
) {
185 buffer_skip_bytes(node_ctx
.textLength
);
189 static void handle_revision(void)
191 repo_commit(rev_ctx
.revision
, rev_ctx
.author
, rev_ctx
.log
, dump_ctx
.uuid
,
192 dump_ctx
.url
, rev_ctx
.timestamp
);
195 static void svndump_read(char *url
)
199 uint32_t active_ctx
= DUMP_CTX
;
203 while ((t
= buffer_read_line())) {
204 val
= strstr(t
, ": ");
209 if(!strcmp(t
, "UUID")) {
210 dump_ctx
.uuid
= strdup(val
);
211 } else if (!strcmp(t
, "Revision-number")) {
212 if (active_ctx
== NODE_CTX
) handle_node();
213 if (active_ctx
!= DUMP_CTX
) handle_revision();
214 active_ctx
= REV_CTX
;
215 reset_rev_ctx(atoi(val
));
216 } else if (!strcmp(t
, "Node-path")) {
217 if (active_ctx
== NODE_CTX
)
219 active_ctx
= NODE_CTX
;
221 } else if (!strcmp(t
, "Node-kind")) {
222 if (!strcmp(val
, "dir")) {
223 node_ctx
.type
= REPO_MODE_DIR
;
224 } else if (!strcmp(val
, "file")) {
225 node_ctx
.type
= REPO_MODE_BLB
;
227 fprintf(stderr
, "Unknown node-kind: %s\n", val
);
229 } else if (!strcmp(t
, "Node-action")) {
230 if (!strcmp(val
, "delete")) {
231 node_ctx
.action
= NODEACT_DELETE
;
232 } else if (!strcmp(val
, "add")) {
233 node_ctx
.action
= NODEACT_ADD
;
234 } else if (!strcmp(val
, "change")) {
235 node_ctx
.action
= NODEACT_CHANGE
;
236 } else if (!strcmp(val
, "replace")) {
237 node_ctx
.action
= NODEACT_REPLACE
;
239 fprintf(stderr
, "Unknown node-action: %s\n", val
);
240 node_ctx
.action
= NODEACT_UNKNOWN
;
242 } else if (!strcmp(t
, "Node-copyfrom-path")) {
243 pool_tok_seq(REPO_MAX_PATH_DEPTH
, node_ctx
.src
, "/", val
);
244 } else if (!strcmp(t
, "Node-copyfrom-rev")) {
245 node_ctx
.srcRev
= atoi(val
);
246 } else if (!strcmp(t
, "Text-content-length")) {
247 node_ctx
.textLength
= atoi(val
);
248 } else if (!strcmp(t
, "Prop-content-length")) {
249 node_ctx
.propLength
= atoi(val
);
250 } else if (!strcmp(t
, "Content-length")) {
253 if (active_ctx
== REV_CTX
) {
255 } else if (active_ctx
== NODE_CTX
) {
257 active_ctx
= REV_CTX
;
259 fprintf(stderr
, "Unexpected content length header: %d\n", len
);
260 buffer_skip_bytes(len
);
264 if (active_ctx
== NODE_CTX
) handle_node();
265 if (active_ctx
!= DUMP_CTX
) handle_revision();
268 static void svndump_reset(void)
273 reset_dump_ctx(NULL
);
275 reset_node_ctx(NULL
);
278 int main(int argc
, char **argv
)
280 svndump_read((argc
> 1) ? argv
[1] : NULL
);