Posted to tcl by mcvoy at Sat Nov 27 00:57:55 GMT 2010view raw
- #!/usr/bin/bk tclsh
-
- /*
- * This is a little SVN to BK importer written in L.
- *
- * It does not attempt to handle
- * renames (other than copy/delete pretty much like SVN)
- * multiple branches
- *
- * -hHOST set the hostname used for checkins
- * --host=host
- *
- * -i incremental, import whatever has been added
- * --incremental
- *
- * -rstart start at this commit in the svn repo (default 1)
- * -rstart..stop set both starting and stopping points (default 1..HEAD)
- * -r..stop stop at this one (default HEAD)
- *
- * -s# do every #th commit (default 1)
- * --stride=#
- *
- * --verify do an export and an extra checkout to verify contents
- *
- * Examples:
- *
- * Capture the early history but only every 10th commit and skip the beginning:
- * $ svn2bk.l -r47..500 -hgoogle.com -s10 file:///svn/groogle/trunk groogle
- *
- * Now do every commit for the rest of the history:
- * $ cd groogle
- * $ svn2bk.l -hgoogle.com -i
- */
-
- typedef struct {
- string user; // username who did the check in
- string date; // 2007-05-13 03:21:54 -0700
- string cmts[]; // array of comments for the commit
- } delta;
-
- delta log{int}; // cache of the entire log
- int revs[]; // ordered list of revs for this branch
- int stride = 1;
- string q = "q"; // -v turns this off and makes bk noisy
-
- void
- main(_argused int ac, string av[])
- {
- string c, host, url, dir;
- int start = 0, stop = 0; // if set, do this range.
- int incremental = 0; // if set, start from last stop
- int verify = 0; // if set, verify each commit
- int i;
- string lopts[] = {
- "host:",
- "incremental",
- "stride:",
- "verify",
- };
-
- while (c = getopt(av, "h:ir:s:v", lopts)) {
- switch (c) {
- case "h":
- case "host":
- host = optarg;
- break;
- case "i":
- case "incremental":
- incremental = 1;
- break;
- case "r":
- if (optarg =~ /(.+)\.\.(.+)/) {
- start = (int)$1;
- stop = (int)$2;
- } else if (optarg =~ /^\.\.(.+)/) {
- stop = (int)$1;
- } else {
- start = (int)optarg;
- }
- break;
- case "s":
- case "stride":
- stride = (int)optarg;
- break;
- case "v": q = ""; break;
- case "verify": verify = 1; break;
- default: usage(-1);
- }
- }
- url = av[optind++];
- dir = av[optind];
- unless (start) start = 1;
- if (host) setenv("BK_HOST", host);
- setenv("CLOCK_DRIFT", "1");
- unless (getenv("BK_HOST")) usage(0);
- unless ((url && dir) || incremental) {
- usage(1);
- }
- if (incremental) {
- unless (isdir(".bk") && isdir(".svn")) {
- fprintf(stderr, "Not at a BK/SVN root\n");
- exit(1);
- }
- env();
- } else if (setup(start, url, dir)) {
- usage(2);
- }
- if (getlog(incremental, ++start, stop)) usage(3);
- for (i = 0; defined(revs[i]); i += stride) {
- assert(cset(revs[i], verify, url) == 0);
- }
- }
-
- /*
- * Create an empty bk repo and the intial svn repo
- * We want to end up with .bk next to .svn
- */
- int
- setup(int start, string url, string dir)
- {
- string bk = "${dir}/bk";
- string buf;
- FILE f;
- int rev;
-
- for (rev = start; rev > 0; rev--) {
- buf = `svn log -r${rev} ${url}`;
- if (buf =~ /[^-]/) break;
- }
- unless (start == rev) {
- fprintf(stderr,
- "%d is not active in this branch. Try %d?\n", start, rev);
- exit(1);
- }
- fprintf(stderr, "=== SVN ${start} ===\n");
- unless (system("svn co -q -r${start} ${url} ${dir}") == 0) {
- return (1);
- }
-
- /*
- * Set up a repo inside the svn repo
- */
- f = fopen("${dir}/.bk_config", "w");
- fprintf(f, "checkout:edit\n");
- fprintf(f, "clockskew:on\n");
- fprintf(f, "partial_check:on\n");
- fclose(f);
- unless (system("bk setup -f -c${dir}/.bk_config ${bk}") == 0) {
- return (1);
- }
- system("tar -C${bk} -cf- . | tar -C${dir} -xf-");
- unlink("${dir}/.bk_config");
- system("rm -rf ${bk}");
- if (isdir(bk)) die("rm failed\n");
- chdir(dir);
- if (isdir("bk")) die("rm failed2\n");
- env();
-
- // Prune the top level one, we'll grep out the others
- system("bk ignore '*/.svn -prune'");
-
- system("bk -cxU | bk ci -a${q}ly'SVN ${start}' -");
- system("bk _eula -a");
- system("bk commit -${q}y'SVN ${start}'");
- return (0);
- }
-
- void
- env(void)
- {
- string svn;
-
- setenv("BK_CONFIG", "clockskew=1!;compression:off!");
- mkdir("BitKeeper/tmp/dotbk");
- setenv("BK_DOTBK", "BitKeeper/tmp/dotbk");
- setenv("_BK_CREATE_MISSING_DIRS", "yes");
- svn = "SVN_I_LOVE_CORRUPTED_WORKING_COPIES" .
- "_SO_DISABLE_SLEEP_FOR_TIMESTAMPS";
- setenv(svn, "yes");
- }
-
- /*
- * Import a SVN commit.
- * We get the updates, then
- * - for each file that is not checked out, svn deleted it so we delete it
- * - for each modified/extra we check those in with the comment/user/date
- * from the log message.
- */
- int
- cset(int rev, int verify, string url)
- {
- FILE f;
- string buf, tmp, svn, bk;
-
- fprintf(stderr, "=== SVN ${rev} ===\n");
- unless (system("svn update -q -r${rev}") == 0) return (1);
- tmp = "BitKeeper/tmp/comments";
- f = fopen(tmp, "w");
- foreach (buf in log{rev}.cmts) {
- fprintf(f, "%s\n", buf);
- }
- fclose(f);
- setenv("BK_USER", log{rev}.user);
- setenv("BK_DATE_TIME_ZONE", log{rev}.date);
- system("bk -U^G rm -f");
- system("bk -xcU | bk ci -a${q}lY${tmp} -");
- f = fopen(tmp, "a");
- fprintf(f, "SVN: %d\n", rev);
- fclose(f);
- system("bk commit -${q}Y${tmp}");
- unless (verify) return (0);
-
- svn = sprintf("/tmp/svn.%d", getpid());
- system("/bin/rm -rf ${svn}");
- unless (system("svn co -q -r${rev} ${url} ${svn}") == 0) {
- die("unable to check out ${rev} in ${svn}");
- }
- bk = sprintf("/tmp/bk.%d", getpid());
- system("/bin/rm -rf ${bk}");
- unless (system("bk export -r+ -tplain . ${bk}") == 0) {
- die("unable to export to ${bk}");
- }
-
- buf = `bk diff -r --exclude=.svn ${svn} ${bk}`;
- // system("/bin/rm -rf ${svn} ${bk}");
- if (length(buf)) die(buf);
- }
-
- /*
- * Load up the log, we'll use it for our commits.
- * ------------------------------------------------------------------------
- * r59 | mcccol | 2007-04-17 18:23:39 -0700 (Tue, 17 Apr 2007) | 4 lines
- *
- * removed logging, started using Debug.error
- *
- * ------------------------------------------------------------------------
- * r60 | mcccol | 2007-04-17 18:25:08 -0700 (Tue, 17 Apr 2007) | 4 lines
- *
- * * Added fixbad to utf8 to repair damaged utf8
- * * made regexps variables to preserver their regexp intrep
- *
- * etc.
- */
- int
- getlog(int incremental, int start, int stop)
- {
- FILE f;
- int rev;
- string cmts[];
- string buf;
- string last_date = "";
-
- if (incremental) {
- start = (int)`svn info | grep Revision: | awk '{print $NF}'`;
- start++;
- }
- if (stop) {
- if (stop <= start) {
- fprintf(stderr, "Already up to or past %d\n", stop);
- exit(1);
- }
- f = popen("svn log -r${start}:${stop} 2>@stderr", "r");
- } else {
- f = popen("svn log -r${start}:HEAD 2>@stderr", "r");
- }
- unless ((buf = <f>) && (buf =~ /^[-]+$/)) {
- done: fprintf(stderr, "Seems like you are up to date.\n");
- return (0);
- }
-
- while (!eof(f)) {
- unless (buf = <f>) {
- assert(eof(f));
- break;
- }
- if (buf =~ /\(no author\)/) buf =~ s/.no author./anon/;
- if (buf =~ /\(no date\)/) buf =~ s/.no date./${last_date}/;
- unless (buf =~ /^r(\d+) \| ([^|]+) \| ([0-9 :\-+]+) /) {
- die("expected rev/date: ${buf}\n");
- }
- rev = (int)$1;
- push(&revs, rev);
- log{rev}.user = (string)$2;
- last_date = log{rev}.date = (string)$3;
- buf = <f>; // toss the blank line
- undef(cmts); // toss previous comments
- while (buf = <f>) {
- if ((length(buf) == 72) && (buf =~ /^[-]+$/)) break;
- push(&cmts, buf);
- }
-
- /*
- * Lose trailing blank lines, they serve no purpose.
- */
- while (defined(cmts[END]) && (cmts[END] =~ /^\s*$/)) {
- pop(&cmts);
- }
- /*
- for (i = length(cmts)-1; i >= 0; i--) {
- unless (cmts[i] =~ /^\s*$/) break;
- cmts[i] = undef;
- }
- */
- log{rev}.cmts = cmts;
- }
- pclose(f);
- unless (length(revs)) goto done;
- return (0);
- }
-
- void
- usage(int which)
- {
- fprintf(stderr, "Barfed on %d.\n", which);
- exit(1);
- }
-