# HG changeset patch # User Chris Cannam # Date 1476100588 -3600 # Node ID 3ad53f43483d65c8c6303140ded43e85ac088359 # Parent ac2e4a54a6a63ebe9dbff61ed658045da8aa39d0 Update hg-fast-export to current version (with fix-broken-bare upstream branch) for compatibility with current git diff -r ac2e4a54a6a6 -r 3ad53f43483d extra/fast-export/Makefile --- a/extra/fast-export/Makefile Tue Jul 19 13:49:56 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -SVN ?= /usr/local/svn -APR_INCLUDES ?= /usr/include/apr-1.0 -CFLAGS += -I${APR_INCLUDES} -I${SVN}/include/subversion-1 -pipe -O2 -std=c99 -LDFLAGS += -L${SVN}/lib -lsvn_fs-1 -lsvn_repos-1 - -all: svn-fast-export svn-archive - -svn-fast-export: svn-fast-export.c -svn-archive: svn-archive.c - -.PHONY: clean - -clean: - rm -rf svn-fast-export svn-archive diff -r ac2e4a54a6a6 -r 3ad53f43483d extra/fast-export/hg-fast-export.py --- a/extra/fast-export/hg-fast-export.py Tue Jul 19 13:49:56 2016 +0100 +++ b/extra/fast-export/hg-fast-export.py Mon Oct 10 12:56:28 2016 +0100 @@ -145,14 +145,23 @@ if max>cfg_export_boundary: sys.stderr.write('Exported %d/%d files\n' % (count,max)) -def sanitize_name(name,what="branch"): +def sanitize_name(name,what="branch", mapping={}): """Sanitize input roughly according to git-check-ref-format(1)""" + # NOTE: Do not update this transform to work around + # incompatibilities on your platform. If you change it and it starts + # modifying names which previously were not touched it will break + # preexisting setups which are doing incremental imports. + # + # Use the -B and -T options to mangle branch and tag names + # instead. If you have a source repository where this is too much + # work to do manually, write a tool that does it for you. + def dot(name): if name[0] == '.': return '_'+name[1:] return name - n=name + n=mapping.get(name,name) p=re.compile('([[ ~^:?\\\\*]|\.\.)') n=p.sub('_', n) if n[-1] in ('/', '.'): n=n[:-1]+'_' @@ -170,11 +179,11 @@ return filename def export_commit(ui,repo,revision,old_marks,max,count,authors, - branchesmap,sob,brmap,hgtags,notes,encoding='',fn_encoding=''): + branchesmap,sob,brmap,hgtags,encoding='',fn_encoding=''): def get_branchname(name): if brmap.has_key(name): return brmap[name] - n=sanitize_name(branchesmap.get(name,name)) + n=sanitize_name(name, "branch", branchesmap) brmap[name]=n return n @@ -235,28 +244,34 @@ export_file_contents(ctx,man,changed,hgtags,fn_encoding) wr() - count=checkpoint(count) - count=generate_note(user,time,timezone,revision,ctx,count,notes) - return count + return checkpoint(count) -def generate_note(user,time,timezone,revision,ctx,count,notes): - if not notes: - return count +def export_note(ui,repo,revision,count,authors,encoding,is_first): + (revnode,_,user,(time,timezone),_,_,_,_)=get_changeset(ui,repo,revision,authors,encoding) + + parents = [p for p in repo.changelog.parentrevs(revision) if p >= 0] + wr('commit refs/notes/hg') wr('committer %s %d %s' % (user,time,timezone)) wr('data 0') + if is_first: + wr('from refs/notes/hg^0') wr('N inline :%d' % (revision+1)) - hg_hash=ctx.hex() + hg_hash=repo.changectx(str(revision)).hex() wr('data %d' % (len(hg_hash))) wr_no_nl(hg_hash) wr() return checkpoint(count) - + + wr('data %d' % (len(desc)+1)) # wtf? + wr(desc) + wr() + def export_tags(ui,repo,old_marks,mapping_cache,count,authors,tagsmap): l=repo.tagslist() for tag,node in l: # Remap the branch name - tag=sanitize_name(tagsmap.get(tag,tag),"tag") + tag=sanitize_name(tag,"tag",tagsmap) # ignore latest revision if tag=='tip': continue # ignore tags to nodes that are missing (ie, 'in the future') @@ -281,6 +296,7 @@ def load_mapping(name, filename): cache={} if not os.path.exists(filename): + sys.stderr.write('Could not open mapping file [%s]\n' % (filename)) return cache f=open(filename,'r') l=0 @@ -311,7 +327,7 @@ break return tip -def verify_heads(ui,repo,cache,force): +def verify_heads(ui,repo,cache,force,branchesmap): branches={} for bn, heads in repo.branchmap().iteritems(): branches[bn] = branchtip(repo, heads) @@ -321,8 +337,9 @@ # get list of hg's branches to verify, don't take all git has for _,_,b in l: b=get_branch(b) - sha1=get_git_sha1(b) - c=cache.get(b) + sanitized_name=sanitize_name(b,"branch",branchesmap) + sha1=get_git_sha1(sanitized_name) + c=cache.get(sanitized_name) if sha1!=c: sys.stderr.write('Error: Branch [%s] modified outside hg-fast-export:' '\n%s (repo) != %s (cache)\n' % (b,sha1,c)) @@ -343,6 +360,10 @@ def hg2git(repourl,m,marksfile,mappingfile,headsfile,tipfile, authors={},branchesmap={},tagsmap={}, sob=False,force=False,hgtags=False,notes=False,encoding='',fn_encoding=''): + def check_cache(filename, contents): + if len(contents) == 0: + sys.stderr.write('Warning: %s does not contain any data, this will probably make an incremental import fail\n' % filename) + _max=int(m) old_marks=load_cache(marksfile,lambda s: int(s)-1) @@ -350,9 +371,15 @@ heads_cache=load_cache(headsfile) state_cache=load_cache(tipfile) + if len(state_cache) != 0: + for (name, data) in [(marksfile, old_marks), + (mappingfile, mapping_cache), + (headsfile, state_cache)]: + check_cache(name, data) + ui,repo=setup_repo(repourl) - if not verify_heads(ui,repo,heads_cache,force): + if not verify_heads(ui,repo,heads_cache,force,branchesmap): return 1 try: @@ -374,7 +401,10 @@ brmap={} for rev in range(min,max): c=export_commit(ui,repo,rev,old_marks,max,c,authors,branchesmap, - sob,brmap,hgtags,notes,encoding,fn_encoding) + sob,brmap,hgtags,encoding,fn_encoding) + if notes: + for rev in range(min,max): + c=export_note(ui,repo,rev,c,authors, encoding, rev == min and min != 0) state_cache['tip']=max state_cache['repo']=repourl diff -r ac2e4a54a6a6 -r 3ad53f43483d extra/fast-export/hg-fast-export.sh --- a/extra/fast-export/hg-fast-export.sh Tue Jul 19 13:49:56 2016 +0100 +++ b/extra/fast-export/hg-fast-export.sh Mon Oct 10 12:56:28 2016 +0100 @@ -48,8 +48,16 @@ echo "$LONG_USAGE" exit 0 esac -. "$(git --exec-path)/git-sh-setup" -cd_to_toplevel + +IS_BARE=$(git rev-parse --is-bare-repository) \ + || (echo "Could not find git repo" ; exit 1) +if test "z$IS_BARE" != ztrue; then + # This is not a bare repo, cd to the toplevel + TOPLEVEL=$(git rev-parse --show-toplevel) \ + || (echo "Could not find git repo toplevel" ; exit 1) + cd $TOPLEVEL || exit 1 +fi +GIT_DIR=$(git rev-parse --git-dir) || (echo "Could not find git repo" ; exit 1) while case "$#" in 0) break ;; esac do diff -r ac2e4a54a6a6 -r 3ad53f43483d extra/fast-export/hg-reset.sh --- a/extra/fast-export/hg-reset.sh Tue Jul 19 13:49:56 2016 +0100 +++ b/extra/fast-export/hg-reset.sh Mon Oct 10 12:56:28 2016 +0100 @@ -24,8 +24,15 @@ -r Mercurial repository to use " -. "$(git --exec-path)/git-sh-setup" -cd_to_toplevel +IS_BARE=$(git rev-parse --is-bare-repository) \ + || (echo "Could not find git repo" ; exit 1) +if test "z$IS_BARE" != ztrue; then + # This is not a bare repo, cd to the toplevel + TOPLEVEL=$(git rev-parse --show-toplevel) \ + || (echo "Could not find git repo toplevel" ; exit 1) + cd $TOPLEVEL || exit 1 +fi +GIT_DIR=$(git rev-parse --git-dir) || (echo "Could not find git repo" ; exit 1) while case "$#" in 0) break ;; esac do diff -r ac2e4a54a6a6 -r 3ad53f43483d extra/fast-export/hg2git.py --- a/extra/fast-export/hg2git.py Tue Jul 19 13:49:56 2016 +0100 +++ b/extra/fast-export/hg2git.py Mon Oct 10 12:56:28 2016 +0100 @@ -7,6 +7,7 @@ import re import os import sys +import subprocess # default git branch name cfg_master='master' @@ -105,12 +106,10 @@ def get_git_sha1(name,type='heads'): try: # use git-rev-parse to support packed refs - cmd="git rev-parse --verify refs/%s/%s 2>%s" % (type,name,os.devnull) - p=os.popen(cmd) - l=p.readline() - p.close() + ref="refs/%s/%s" % (type,name) + l=subprocess.check_output(["git", "rev-parse", "--verify", "--quiet", ref]) if l == None or len(l) == 0: return None return l[0:40] - except IOError: + except subprocess.CalledProcessError: return None diff -r ac2e4a54a6a6 -r 3ad53f43483d extra/fast-export/svn-archive.c --- a/extra/fast-export/svn-archive.c Tue Jul 19 13:49:56 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,240 +0,0 @@ -/* - * svn-archive.c - * ---------- - * Walk through a given revision of a local Subversion repository and export - * all of the contents as a tarfile. - * - * Author: Chris Lee - * License: MIT - */ - -#define _XOPEN_SOURCE -#include -#include -#include -#include - -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif - -#include -#include -#include -#include - -#include -#include -#include -#include - -#undef SVN_ERR -#define SVN_ERR(expr) SVN_INT_ERR(expr) -#define apr_sane_push(arr, contents) *(char **)apr_array_push(arr) = contents - -#define TRUNK "/trunk" - -static time_t archive_time; - -time_t get_epoch(char *svn_date) -{ - struct tm tm = {0}; - char *date = malloc(strlen(svn_date) * sizeof(char *)); - strncpy(date, svn_date, strlen(svn_date) - 8); - strptime(date, "%Y-%m-%dT%H:%M:%S", &tm); - free(date); - return mktime(&tm); -} - -int tar_header(apr_pool_t *pool, char *path, char *node, size_t f_size) -{ - char buf[512]; - unsigned int i, checksum; - svn_boolean_t is_dir; - - memset(buf, 0, sizeof(buf)); - - if ((strlen(path) == 0) && (strlen(node) == 0)) { - return 0; - } - - if (strlen(node) == 0) { - is_dir = 1; - } else { - is_dir = 0; - } - - if (strlen(path) == 0) { - strncpy(buf, apr_psprintf(pool, "%s", node), 99); - } else if (strlen(path) + strlen(node) < 100) { - strncpy(buf, apr_psprintf(pool, "%s/%s", path+1, node), 99); - } else { - fprintf(stderr, "really long file path...\n"); - strncpy(&buf[0], node, 99); - strncpy(&buf[345], path+1, 154); - } - - strncpy(&buf[100], apr_psprintf(pool, "%07o", (is_dir ? 0755 : 0644)), 7); - strncpy(&buf[108], apr_psprintf(pool, "%07o", 1000), 7); - strncpy(&buf[116], apr_psprintf(pool, "%07o", 1000), 7); - strncpy(&buf[124], apr_psprintf(pool, "%011lo", f_size), 11); - strncpy(&buf[136], apr_psprintf(pool, "%011lo", archive_time), 11); - strncpy(&buf[156], (is_dir ? "5" : "0"), 1); - strncpy(&buf[257], "ustar ", 8); - strncpy(&buf[265], "clee", 31); - strncpy(&buf[297], "clee", 31); - // strncpy(&buf[329], apr_psprintf(pool, "%07o", 0), 7); - // strncpy(&buf[337], apr_psprintf(pool, "%07o", 0), 7); - - strncpy(&buf[148], " ", 8); - checksum = 0; - for (i = 0; i < sizeof(buf); i++) { - checksum += buf[i]; - } - strncpy(&buf[148], apr_psprintf(pool, "%07o", checksum & 0x1fffff), 7); - - fwrite(buf, sizeof(char), sizeof(buf), stdout); - - return 0; -} - -int tar_footer() -{ - char block[1024]; - memset(block, 0, sizeof(block)); - fwrite(block, sizeof(char), sizeof(block), stdout); -} - -int dump_blob(svn_fs_root_t *root, char *prefix, char *path, char *node, apr_pool_t *pool) -{ - char *full_path, buf[512]; - apr_size_t len; - svn_stream_t *stream; - svn_filesize_t stream_length; - - full_path = apr_psprintf(pool, "%s%s/%s", prefix, path, node); - - SVN_ERR(svn_fs_file_length(&stream_length, root, full_path, pool)); - SVN_ERR(svn_fs_file_contents(&stream, root, full_path, pool)); - - tar_header(pool, path, node, stream_length); - - do { - len = sizeof(buf); - memset(buf, '\0', sizeof(buf)); - SVN_ERR(svn_stream_read(stream, buf, &len)); - fwrite(buf, sizeof(char), sizeof(buf), stdout); - } while (len == sizeof(buf)); - - return 0; -} - -int dump_tree(svn_fs_root_t *root, char *prefix, char *path, apr_pool_t *pool) -{ - const void *key; - void *val; - char *node, *subpath, *full_path; - - apr_pool_t *subpool; - apr_hash_t *dir_entries; - apr_hash_index_t *i; - - svn_boolean_t is_dir; - - tar_header(pool, path, "", 0); - - SVN_ERR(svn_fs_dir_entries(&dir_entries, root, apr_psprintf(pool, "%s/%s", prefix, path), pool)); - - subpool = svn_pool_create(pool); - - for (i = apr_hash_first(pool, dir_entries); i; i = apr_hash_next(i)) { - svn_pool_clear(subpool); - apr_hash_this(i, &key, NULL, &val); - node = (char *)key; - - subpath = apr_psprintf(subpool, "%s/%s", path, node); - full_path = apr_psprintf(subpool, "%s%s", prefix, subpath); - - svn_fs_is_dir(&is_dir, root, full_path, subpool); - - if (is_dir) { - dump_tree(root, prefix, subpath, subpool); - } else { - dump_blob(root, prefix, path, node, subpool); - } - } - - svn_pool_destroy(subpool); - - return 0; -} - -int crawl_filesystem(char *repos_path, char *root_path, apr_pool_t *pool) -{ - char *path; - - apr_hash_t *props; - apr_hash_index_t *i; - - svn_repos_t *repos; - svn_fs_t *fs; - svn_string_t *svndate; - svn_revnum_t youngest_rev, export_rev; - svn_fs_root_t *fs_root; - - SVN_ERR(svn_fs_initialize(pool)); - SVN_ERR(svn_repos_open(&repos, repos_path, pool)); - if ((fs = svn_repos_fs(repos)) == NULL) - return -1; - SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool)); - - export_rev = youngest_rev; - - SVN_ERR(svn_fs_revision_root(&fs_root, fs, export_rev, pool)); - SVN_ERR(svn_fs_revision_proplist(&props, fs, export_rev, pool)); - - svndate = apr_hash_get(props, "svn:date", APR_HASH_KEY_STRING); - archive_time = get_epoch((char *)svndate->data); - - fprintf(stderr, "Exporting archive of r%ld... \n", export_rev); - - dump_tree(fs_root, root_path, "", pool); - - tar_footer(); - - fprintf(stderr, "done!\n"); - - return 0; -} - -int main(int argc, char *argv[]) -{ - apr_pool_t *pool; - apr_getopt_t *options; - - apr_getopt_option_t long_options[] = { - { "help", 'h', 0 }, - { "prefix", 'p', 0 }, - { "basename", 'b', 0 }, - { "revision", 'r', 0 }, - { NULL, 0, 0 } - }; - - if (argc < 2) { - fprintf(stderr, "usage: %s REPOS_PATH [prefix]\n", argv[0]); - return -1; - } - - if (apr_initialize() != APR_SUCCESS) { - fprintf(stderr, "You lose at apr_initialize().\n"); - return -1; - } - - pool = svn_pool_create(NULL); - - crawl_filesystem(argv[1], (argc == 3 ? argv[2] : TRUNK), pool); - - apr_terminate(); - - return 0; -} diff -r ac2e4a54a6a6 -r 3ad53f43483d extra/fast-export/svn-fast-export.c --- a/extra/fast-export/svn-fast-export.c Tue Jul 19 13:49:56 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,187 +0,0 @@ -/* - * svn-fast-export.c - * ---------- - * Walk through each revision of a local Subversion repository and export it - * in a stream that git-fast-import can consume. - * - * Author: Chris Lee - * License: MIT - */ - -#define _XOPEN_SOURCE -#include -#include -#include -#include - -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif - -#include -#include -#include - -#include -#include -#include -#include - -#undef SVN_ERR -#define SVN_ERR(expr) SVN_INT_ERR(expr) -#define apr_sane_push(arr, contents) *(char **)apr_array_push(arr) = contents - -#define TRUNK "/trunk/" - -time_t get_epoch(char *svn_date) -{ - struct tm tm = {0}; - char *date = malloc(strlen(svn_date) * sizeof(char *)); - strncpy(date, svn_date, strlen(svn_date) - 8); - strptime(date, "%Y-%m-%dT%H:%M:%S", &tm); - free(date); - return mktime(&tm); -} - -int dump_blob(svn_fs_root_t *root, char *full_path, apr_pool_t *pool) -{ - apr_size_t len; - svn_stream_t *stream, *outstream; - svn_filesize_t stream_length; - - SVN_ERR(svn_fs_file_length(&stream_length, root, full_path, pool)); - SVN_ERR(svn_fs_file_contents(&stream, root, full_path, pool)); - - fprintf(stdout, "data %lu\n", stream_length); - fflush(stdout); - - SVN_ERR(svn_stream_for_stdout(&outstream, pool)); - SVN_ERR(svn_stream_copy(stream, outstream, pool)); - - fprintf(stdout, "\n"); - fflush(stdout); - - return 0; -} - -int export_revision(svn_revnum_t rev, svn_fs_t *fs, apr_pool_t *pool) -{ - unsigned int mark; - const void *key; - void *val; - char *path, *file_change; - apr_pool_t *revpool; - apr_hash_t *changes, *props; - apr_hash_index_t *i; - apr_array_header_t *file_changes; - svn_string_t *author, *committer, *svndate, *svnlog; - svn_boolean_t is_dir; - svn_fs_root_t *fs_root; - svn_fs_path_change_t *change; - - fprintf(stderr, "Exporting revision %ld... ", rev); - - SVN_ERR(svn_fs_revision_root(&fs_root, fs, rev, pool)); - SVN_ERR(svn_fs_paths_changed(&changes, fs_root, pool)); - SVN_ERR(svn_fs_revision_proplist(&props, fs, rev, pool)); - - revpool = svn_pool_create(pool); - - file_changes = apr_array_make(pool, apr_hash_count(changes), sizeof(char *)); - mark = 1; - for (i = apr_hash_first(pool, changes); i; i = apr_hash_next(i)) { - svn_pool_clear(revpool); - apr_hash_this(i, &key, NULL, &val); - path = (char *)key; - change = (svn_fs_path_change_t *)val; - - SVN_ERR(svn_fs_is_dir(&is_dir, fs_root, path, revpool)); - - if (is_dir || strncmp(TRUNK, path, strlen(TRUNK))) { - continue; - } - - if (change->change_kind == svn_fs_path_change_delete) { - apr_sane_push(file_changes, (char *)svn_string_createf(pool, "D %s", path + strlen(TRUNK))->data); - } else { - apr_sane_push(file_changes, (char *)svn_string_createf(pool, "M 644 :%u %s", mark, path + strlen(TRUNK))->data); - fprintf(stdout, "blob\nmark :%u\n", mark++); - dump_blob(fs_root, (char *)path, revpool); - } - } - - if (file_changes->nelts == 0) { - fprintf(stderr, "skipping.\n"); - svn_pool_destroy(revpool); - return 0; - } - - author = apr_hash_get(props, "svn:author", APR_HASH_KEY_STRING); - if (svn_string_isempty(author)) - author = svn_string_create("nobody", pool); - svndate = apr_hash_get(props, "svn:date", APR_HASH_KEY_STRING); - svnlog = apr_hash_get(props, "svn:log", APR_HASH_KEY_STRING); - - fprintf(stdout, "commit refs/heads/master\n"); - fprintf(stdout, "committer %s <%s@localhost> %ld -0000\n", author->data, author->data, get_epoch((char *)svndate->data)); - fprintf(stdout, "data %d\n", svnlog->len); - fputs(svnlog->data, stdout); - fprintf(stdout, "\n"); - fputs(apr_array_pstrcat(pool, file_changes, '\n'), stdout); - fprintf(stdout, "\n\n"); - fflush(stdout); - - svn_pool_destroy(revpool); - - fprintf(stderr, "done!\n"); - - return 0; -} - -int crawl_revisions(char *repos_path) -{ - apr_pool_t *pool, *subpool; - svn_fs_t *fs; - svn_repos_t *repos; - svn_revnum_t youngest_rev, min_rev, max_rev, rev; - - pool = svn_pool_create(NULL); - - SVN_ERR(svn_fs_initialize(pool)); - SVN_ERR(svn_repos_open(&repos, repos_path, pool)); - if ((fs = svn_repos_fs(repos)) == NULL) - return -1; - SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool)); - - min_rev = 1; - max_rev = youngest_rev; - - subpool = svn_pool_create(pool); - for (rev = min_rev; rev <= max_rev; rev++) { - svn_pool_clear(subpool); - export_revision(rev, fs, subpool); - } - - svn_pool_destroy(pool); - - return 0; -} - -int main(int argc, char *argv[]) -{ - if (argc != 2) { - fprintf(stderr, "usage: %s REPOS_PATH\n", argv[0]); - return -1; - } - - if (apr_initialize() != APR_SUCCESS) { - fprintf(stderr, "You lose at apr_initialize().\n"); - return -1; - } - - crawl_revisions(argv[1]); - - apr_terminate(); - - return 0; -} diff -r ac2e4a54a6a6 -r 3ad53f43483d extra/fast-export/svn-fast-export.py --- a/extra/fast-export/svn-fast-export.py Tue Jul 19 13:49:56 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,160 +0,0 @@ -#!/usr/bin/python -# -# svn-fast-export.py -# ---------- -# Walk through each revision of a local Subversion repository and export it -# in a stream that git-fast-import can consume. -# -# Author: Chris Lee -# License: MIT - -trunk_path = '/trunk/' -branches_path = '/branches/' -tags_path = '/tags/' - -first_rev = 1 -final_rev = 0 - -import sys, os.path -from optparse import OptionParser -from time import mktime, strptime -from svn.fs import svn_fs_file_length, svn_fs_file_contents, svn_fs_is_dir, svn_fs_revision_root, svn_fs_youngest_rev, svn_fs_revision_proplist, svn_fs_paths_changed -from svn.core import svn_pool_create, svn_pool_clear, svn_pool_destroy, svn_stream_for_stdout, svn_stream_copy, svn_stream_close, run_app -from svn.repos import svn_repos_open, svn_repos_fs - -ct_short = ['M', 'A', 'D', 'R', 'X'] - -def dump_file_blob(root, full_path, pool): - stream_length = svn_fs_file_length(root, full_path, pool) - stream = svn_fs_file_contents(root, full_path, pool) - sys.stdout.write("data %s\n" % stream_length) - sys.stdout.flush() - ostream = svn_stream_for_stdout(pool) - svn_stream_copy(stream, ostream, pool) - svn_stream_close(ostream) - sys.stdout.write("\n") - - -def export_revision(rev, repo, fs, pool): - sys.stderr.write("Exporting revision %s... " % rev) - - revpool = svn_pool_create(pool) - svn_pool_clear(revpool) - - # Open a root object representing the youngest (HEAD) revision. - root = svn_fs_revision_root(fs, rev, revpool) - - # And the list of what changed in this revision. - changes = svn_fs_paths_changed(root, revpool) - - i = 1 - marks = {} - file_changes = [] - - for path, change_type in changes.iteritems(): - c_t = ct_short[change_type.change_kind] - if svn_fs_is_dir(root, path, revpool): - continue - - if not path.startswith(trunk_path): - # We don't handle branches. Or tags. Yet. - pass - else: - if c_t == 'D': - file_changes.append("D %s" % path.replace(trunk_path, '')) - else: - marks[i] = path.replace(trunk_path, '') - file_changes.append("M 644 :%s %s" % (i, marks[i])) - sys.stdout.write("blob\nmark :%s\n" % i) - dump_file_blob(root, path, revpool) - i += 1 - - # Get the commit author and message - props = svn_fs_revision_proplist(fs, rev, revpool) - - # Do the recursive crawl. - if props.has_key('svn:author'): - author = "%s <%s@localhost>" % (props['svn:author'], props['svn:author']) - else: - author = 'nobody ' - - if len(file_changes) == 0: - svn_pool_destroy(revpool) - sys.stderr.write("skipping.\n") - return - - svndate = props['svn:date'][0:-8] - commit_time = mktime(strptime(svndate, '%Y-%m-%dT%H:%M:%S')) - sys.stdout.write("commit refs/heads/master\n") - sys.stdout.write("committer %s %s -0000\n" % (author, int(commit_time))) - sys.stdout.write("data %s\n" % len(props['svn:log'])) - sys.stdout.write(props['svn:log']) - sys.stdout.write("\n") - sys.stdout.write('\n'.join(file_changes)) - sys.stdout.write("\n\n") - - svn_pool_destroy(revpool) - - sys.stderr.write("done!\n") - - #if rev % 1000 == 0: - # sys.stderr.write("gc: %s objects\n" % len(gc.get_objects())) - # sleep(5) - - -def crawl_revisions(pool, repos_path): - """Open the repository at REPOS_PATH, and recursively crawl all its - revisions.""" - global final_rev - - # Open the repository at REPOS_PATH, and get a reference to its - # versioning filesystem. - repos_obj = svn_repos_open(repos_path, pool) - fs_obj = svn_repos_fs(repos_obj) - - # Query the current youngest revision. - youngest_rev = svn_fs_youngest_rev(fs_obj, pool) - - - first_rev = 1 - if final_rev == 0: - final_rev = youngest_rev - for rev in xrange(first_rev, final_rev + 1): - export_revision(rev, repos_obj, fs_obj, pool) - - -if __name__ == '__main__': - usage = '%prog [options] REPOS_PATH' - parser = OptionParser() - parser.set_usage(usage) - parser.add_option('-f', '--final-rev', help='Final revision to import', - dest='final_rev', metavar='FINAL_REV', type='int') - parser.add_option('-t', '--trunk-path', help='Path in repo to /trunk', - dest='trunk_path', metavar='TRUNK_PATH') - parser.add_option('-b', '--branches-path', help='Path in repo to /branches', - dest='branches_path', metavar='BRANCHES_PATH') - parser.add_option('-T', '--tags-path', help='Path in repo to /tags', - dest='tags_path', metavar='TAGS_PATH') - (options, args) = parser.parse_args() - - if options.trunk_path != None: - trunk_path = options.trunk_path - if options.branches_path != None: - branches_path = options.branches_path - if options.tags_path != None: - tags_path = options.tags_path - if options.final_rev != None: - final_rev = options.final_rev - - if len(args) != 1: - parser.print_help() - sys.exit(2) - - # Canonicalize (enough for Subversion, at least) the repository path. - repos_path = os.path.normpath(args[0]) - if repos_path == '.': - repos_path = '' - - # Call the app-wrapper, which takes care of APR initialization/shutdown - # and the creation and cleanup of our top-level memory pool. - run_app(crawl_revisions, repos_path)