4 * Walk through each revision of a local Subversion repository and export it
5 * in a stream that git-fast-import can consume.
7 * Author: Chris Lee <clee@kde.org>
8 * License: MIT <http://www.opensource.org/licenses/mit-license.php>
21 #include <apr_general.h>
23 #include <apr_getopt.h>
25 #include <svn_types.h>
26 #include <svn_pools.h>
27 #include <svn_repos.h>
30 #define SVN_ERR(expr) SVN_INT_ERR(expr)
31 #define apr_sane_push(arr, contents) *(char **)apr_array_push(arr) = contents
33 #define TRUNK "/trunk/"
35 time_t get_epoch(char *svn_date
)
38 char *date
= malloc(strlen(svn_date
) * sizeof(char *));
39 strncpy(date
, svn_date
, strlen(svn_date
) - 8);
40 strptime(date
, "%Y-%m-%dT%H:%M:%S", &tm
);
45 int dump_blob(svn_fs_root_t
*root
, char *full_path
, apr_pool_t
*pool
)
47 svn_filesize_t stream_length
;
48 svn_stream_t
*stream
, *outstream
;
52 SVN_ERR(svn_fs_file_length(&stream_length
, root
, full_path
, pool
));
53 SVN_ERR(svn_fs_file_contents(&stream
, root
, full_path
, pool
));
55 fprintf(stdout
, "data %lu\n", stream_length
);
58 SVN_ERR(svn_stream_for_stdout(&outstream
, pool
));
59 SVN_ERR(svn_stream_copy(stream
, outstream
, pool
));
61 fprintf(stdout
, "\n");
67 int export_revision(svn_revnum_t rev
, svn_repos_t
*repo
, svn_fs_t
*fs
, apr_pool_t
*pool
)
69 apr_array_header_t
*file_changes
;
70 apr_hash_t
*changes
, *props
;
74 svn_fs_path_change_t
*change
;
75 svn_fs_root_t
*root_obj
;
77 svn_string_t
*author
, *committer
, *svndate
, *svnlog
;
79 char *path
, *file_change
;
84 fprintf(stderr
, "Exporting revision %ld... ", rev
);
86 SVN_ERR(svn_fs_revision_root(&root_obj
, fs
, rev
, pool
));
87 SVN_ERR(svn_fs_paths_changed(&changes
, root_obj
, pool
));
88 SVN_ERR(svn_fs_revision_proplist(&props
, fs
, rev
, pool
));
90 revpool
= svn_pool_create(pool
);
92 file_changes
= apr_array_make(pool
, apr_hash_count(changes
), sizeof(char *));
94 for (i
= apr_hash_first(pool
, changes
); i
; i
= apr_hash_next(i
)) {
95 svn_pool_clear(revpool
);
96 apr_hash_this(i
, &key
, NULL
, &val
);
98 change
= (svn_fs_path_change_t
*)val
;
100 SVN_ERR(svn_fs_is_dir(&is_dir
, root_obj
, path
, revpool
));
102 if (is_dir
|| strncmp(TRUNK
, path
, strlen(TRUNK
))) {
106 if (change
->change_kind
== svn_fs_path_change_delete
) {
107 *(char **)apr_array_push(file_changes
) = ((char *)svn_string_createf(pool
, "D %s", path
+ strlen(TRUNK
))->data
);
110 *(char **)apr_array_push(file_changes
) = (char *)svn_string_createf(pool
, "M 644 :%u %s", mark
, path
+ strlen(TRUNK
))->data
;
111 fprintf(stdout
, "blob\nmark :%u\n", mark
++);
112 dump_blob(root_obj
, (char *)path
, revpool
);
116 if (file_changes
->nelts
== 0) {
117 fprintf(stderr
, "skipping.\n");
118 svn_pool_destroy(revpool
);
122 author
= apr_hash_get(props
, "svn:author", APR_HASH_KEY_STRING
);
123 if (!author
) { author
= svn_string_create("nobody", pool
); }
124 svndate
= apr_hash_get(props
, "svn:date", APR_HASH_KEY_STRING
);
125 svnlog
= apr_hash_get(props
, "svn:log", APR_HASH_KEY_STRING
);
127 fprintf(stdout
, "commit refs/heads/master\n");
128 fprintf(stdout
, "committer %s <%s@localhost> %ld -0000\n", author
->data
, author
->data
, get_epoch((char *)svndate
->data
));
129 fprintf(stdout
, "data %d\n", svnlog
->len
);
130 fputs(svnlog
->data
, stdout
);
131 fprintf(stdout
, "\n");
132 fputs(apr_array_pstrcat(pool
, file_changes
, '\n'), stdout
);
133 fprintf(stdout
, "\n\n");
136 svn_pool_destroy(revpool
);
138 fprintf(stderr
, "done!\n");
143 int crawl_revisions(char *repos_path
)
145 apr_pool_t
*pool
, *subpool
;
147 svn_revnum_t youngest_rev
, min_rev
, max_rev
, rev
;
150 pool
= svn_pool_create(NULL
);
152 SVN_ERR(svn_repos_open(&repos
, repos_path
, pool
));
154 fs
= svn_repos_fs(repos
);
156 SVN_ERR(svn_fs_initialize(pool
));
157 SVN_ERR(svn_fs_youngest_rev(&youngest_rev
, fs
, pool
));
160 max_rev
= youngest_rev
;
162 subpool
= svn_pool_create(pool
);
163 for (rev
= min_rev
; rev
<= max_rev
; rev
++) {
164 svn_pool_clear(subpool
);
165 export_revision(rev
, repos
, fs
, subpool
);
168 svn_pool_destroy(pool
);
173 int main(int argc
, char *argv
[])
176 fprintf(stderr
, "usage: %s REPOS_PATH\n", argv
[0]);
180 if (apr_initialize() != APR_SUCCESS
) {
181 fprintf(stderr
, "You lose at apr_initialize().\n");
185 crawl_revisions(argv
[1]);