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>
22 #include <apr_getopt.h>
23 #include <apr_general.h>
26 #include <svn_repos.h>
27 #include <svn_pools.h>
28 #include <svn_types.h>
31 #define SVN_ERR(expr) SVN_INT_ERR(expr)
32 #define apr_sane_push(arr, contents) *(char **)apr_array_push(arr) = contents
34 #define TRUNK "/trunk/"
36 time_t get_epoch(char *svn_date
)
39 char *date
= malloc(strlen(svn_date
) * sizeof(char *));
40 strncpy(date
, svn_date
, strlen(svn_date
) - 8);
41 strptime(date
, "%Y-%m-%dT%H:%M:%S", &tm
);
46 int dump_blob(svn_fs_root_t
*root
, char *full_path
, apr_pool_t
*pool
)
49 svn_stream_t
*stream
, *outstream
;
50 svn_filesize_t stream_length
;
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_fs_t
*fs
, apr_pool_t
*pool
)
72 char *path
, *file_change
;
74 apr_hash_t
*changes
, *props
;
76 apr_array_header_t
*file_changes
;
77 svn_string_t
*author
, *committer
, *svndate
, *svnlog
;
79 svn_fs_root_t
*fs_root
;
80 svn_fs_path_change_t
*change
;
82 fprintf(stderr
, "Exporting revision %ld... ", rev
);
84 SVN_ERR(svn_fs_revision_root(&fs_root
, fs
, rev
, pool
));
85 SVN_ERR(svn_fs_paths_changed(&changes
, fs_root
, pool
));
86 SVN_ERR(svn_fs_revision_proplist(&props
, fs
, rev
, pool
));
88 revpool
= svn_pool_create(pool
);
90 file_changes
= apr_array_make(pool
, apr_hash_count(changes
), sizeof(char *));
92 for (i
= apr_hash_first(pool
, changes
); i
; i
= apr_hash_next(i
)) {
93 svn_pool_clear(revpool
);
94 apr_hash_this(i
, &key
, NULL
, &val
);
96 change
= (svn_fs_path_change_t
*)val
;
98 SVN_ERR(svn_fs_is_dir(&is_dir
, fs_root
, path
, revpool
));
100 if (is_dir
|| strncmp(TRUNK
, path
, strlen(TRUNK
))) {
104 if (change
->change_kind
== svn_fs_path_change_delete
) {
105 apr_sane_push(file_changes
, (char *)svn_string_createf(pool
, "D %s", path
+ strlen(TRUNK
))->data
);
107 apr_sane_push(file_changes
, (char *)svn_string_createf(pool
, "M 644 :%u %s", mark
, path
+ strlen(TRUNK
))->data
);
108 fprintf(stdout
, "blob\nmark :%u\n", mark
++);
109 dump_blob(fs_root
, (char *)path
, revpool
);
113 if (file_changes
->nelts
== 0) {
114 fprintf(stderr
, "skipping.\n");
115 svn_pool_destroy(revpool
);
119 author
= apr_hash_get(props
, "svn:author", APR_HASH_KEY_STRING
);
120 if (svn_string_isempty(author
))
121 author
= svn_string_create("nobody", pool
);
122 svndate
= apr_hash_get(props
, "svn:date", APR_HASH_KEY_STRING
);
123 svnlog
= apr_hash_get(props
, "svn:log", APR_HASH_KEY_STRING
);
125 fprintf(stdout
, "commit refs/heads/master\n");
126 fprintf(stdout
, "committer %s <%s@localhost> %ld -0000\n", author
->data
, author
->data
, get_epoch((char *)svndate
->data
));
127 fprintf(stdout
, "data %d\n", svnlog
->len
);
128 fputs(svnlog
->data
, stdout
);
129 fprintf(stdout
, "\n");
130 fputs(apr_array_pstrcat(pool
, file_changes
, '\n'), stdout
);
131 fprintf(stdout
, "\n\n");
134 svn_pool_destroy(revpool
);
136 fprintf(stderr
, "done!\n");
141 int crawl_revisions(char *repos_path
)
143 apr_pool_t
*pool
, *subpool
;
146 svn_revnum_t youngest_rev
, min_rev
, max_rev
, rev
;
148 pool
= svn_pool_create(NULL
);
150 SVN_ERR(svn_fs_initialize(pool
));
151 SVN_ERR(svn_repos_open(&repos
, repos_path
, pool
));
152 if ((fs
= svn_repos_fs(repos
)) == NULL
)
154 SVN_ERR(svn_fs_youngest_rev(&youngest_rev
, fs
, pool
));
157 max_rev
= youngest_rev
;
159 subpool
= svn_pool_create(pool
);
160 for (rev
= min_rev
; rev
<= max_rev
; rev
++) {
161 svn_pool_clear(subpool
);
162 export_revision(rev
, fs
, subpool
);
165 svn_pool_destroy(pool
);
170 int main(int argc
, char *argv
[])
173 fprintf(stderr
, "usage: %s REPOS_PATH\n", argv
[0]);
177 if (apr_initialize() != APR_SUCCESS
) {
178 fprintf(stderr
, "You lose at apr_initialize().\n");
182 crawl_revisions(argv
[1]);