Posted to tcl by mcvoy at Sat Nov 27 00:57:55 GMT 2010view raw

  1. #!/usr/bin/bk tclsh
  2.  
  3. /*
  4. * This is a little SVN to BK importer written in L.
  5. *
  6. * It does not attempt to handle
  7. * renames (other than copy/delete pretty much like SVN)
  8. * multiple branches
  9. *
  10. * -hHOST set the hostname used for checkins
  11. * --host=host
  12. *
  13. * -i incremental, import whatever has been added
  14. * --incremental
  15. *
  16. * -rstart start at this commit in the svn repo (default 1)
  17. * -rstart..stop set both starting and stopping points (default 1..HEAD)
  18. * -r..stop stop at this one (default HEAD)
  19. *
  20. * -s# do every #th commit (default 1)
  21. * --stride=#
  22. *
  23. * --verify do an export and an extra checkout to verify contents
  24. *
  25. * Examples:
  26. *
  27. * Capture the early history but only every 10th commit and skip the beginning:
  28. * $ svn2bk.l -r47..500 -hgoogle.com -s10 file:///svn/groogle/trunk groogle
  29. *
  30. * Now do every commit for the rest of the history:
  31. * $ cd groogle
  32. * $ svn2bk.l -hgoogle.com -i
  33. */
  34.  
  35. typedef struct {
  36. string user; // username who did the check in
  37. string date; // 2007-05-13 03:21:54 -0700
  38. string cmts[]; // array of comments for the commit
  39. } delta;
  40.  
  41. delta log{int}; // cache of the entire log
  42. int revs[]; // ordered list of revs for this branch
  43. int stride = 1;
  44. string q = "q"; // -v turns this off and makes bk noisy
  45.  
  46. void
  47. main(_argused int ac, string av[])
  48. {
  49. string c, host, url, dir;
  50. int start = 0, stop = 0; // if set, do this range.
  51. int incremental = 0; // if set, start from last stop
  52. int verify = 0; // if set, verify each commit
  53. int i;
  54. string lopts[] = {
  55. "host:",
  56. "incremental",
  57. "stride:",
  58. "verify",
  59. };
  60.  
  61. while (c = getopt(av, "h:ir:s:v", lopts)) {
  62. switch (c) {
  63. case "h":
  64. case "host":
  65. host = optarg;
  66. break;
  67. case "i":
  68. case "incremental":
  69. incremental = 1;
  70. break;
  71. case "r":
  72. if (optarg =~ /(.+)\.\.(.+)/) {
  73. start = (int)$1;
  74. stop = (int)$2;
  75. } else if (optarg =~ /^\.\.(.+)/) {
  76. stop = (int)$1;
  77. } else {
  78. start = (int)optarg;
  79. }
  80. break;
  81. case "s":
  82. case "stride":
  83. stride = (int)optarg;
  84. break;
  85. case "v": q = ""; break;
  86. case "verify": verify = 1; break;
  87. default: usage(-1);
  88. }
  89. }
  90. url = av[optind++];
  91. dir = av[optind];
  92. unless (start) start = 1;
  93. if (host) setenv("BK_HOST", host);
  94. setenv("CLOCK_DRIFT", "1");
  95. unless (getenv("BK_HOST")) usage(0);
  96. unless ((url && dir) || incremental) {
  97. usage(1);
  98. }
  99. if (incremental) {
  100. unless (isdir(".bk") && isdir(".svn")) {
  101. fprintf(stderr, "Not at a BK/SVN root\n");
  102. exit(1);
  103. }
  104. env();
  105. } else if (setup(start, url, dir)) {
  106. usage(2);
  107. }
  108. if (getlog(incremental, ++start, stop)) usage(3);
  109. for (i = 0; defined(revs[i]); i += stride) {
  110. assert(cset(revs[i], verify, url) == 0);
  111. }
  112. }
  113.  
  114. /*
  115. * Create an empty bk repo and the intial svn repo
  116. * We want to end up with .bk next to .svn
  117. */
  118. int
  119. setup(int start, string url, string dir)
  120. {
  121. string bk = "${dir}/bk";
  122. string buf;
  123. FILE f;
  124. int rev;
  125.  
  126. for (rev = start; rev > 0; rev--) {
  127. buf = `svn log -r${rev} ${url}`;
  128. if (buf =~ /[^-]/) break;
  129. }
  130. unless (start == rev) {
  131. fprintf(stderr,
  132. "%d is not active in this branch. Try %d?\n", start, rev);
  133. exit(1);
  134. }
  135. fprintf(stderr, "=== SVN ${start} ===\n");
  136. unless (system("svn co -q -r${start} ${url} ${dir}") == 0) {
  137. return (1);
  138. }
  139.  
  140. /*
  141. * Set up a repo inside the svn repo
  142. */
  143. f = fopen("${dir}/.bk_config", "w");
  144. fprintf(f, "checkout:edit\n");
  145. fprintf(f, "clockskew:on\n");
  146. fprintf(f, "partial_check:on\n");
  147. fclose(f);
  148. unless (system("bk setup -f -c${dir}/.bk_config ${bk}") == 0) {
  149. return (1);
  150. }
  151. system("tar -C${bk} -cf- . | tar -C${dir} -xf-");
  152. unlink("${dir}/.bk_config");
  153. system("rm -rf ${bk}");
  154. if (isdir(bk)) die("rm failed\n");
  155. chdir(dir);
  156. if (isdir("bk")) die("rm failed2\n");
  157. env();
  158.  
  159. // Prune the top level one, we'll grep out the others
  160. system("bk ignore '*/.svn -prune'");
  161.  
  162. system("bk -cxU | bk ci -a${q}ly'SVN ${start}' -");
  163. system("bk _eula -a");
  164. system("bk commit -${q}y'SVN ${start}'");
  165. return (0);
  166. }
  167.  
  168. void
  169. env(void)
  170. {
  171. string svn;
  172.  
  173. setenv("BK_CONFIG", "clockskew=1!;compression:off!");
  174. mkdir("BitKeeper/tmp/dotbk");
  175. setenv("BK_DOTBK", "BitKeeper/tmp/dotbk");
  176. setenv("_BK_CREATE_MISSING_DIRS", "yes");
  177. svn = "SVN_I_LOVE_CORRUPTED_WORKING_COPIES" .
  178. "_SO_DISABLE_SLEEP_FOR_TIMESTAMPS";
  179. setenv(svn, "yes");
  180. }
  181.  
  182. /*
  183. * Import a SVN commit.
  184. * We get the updates, then
  185. * - for each file that is not checked out, svn deleted it so we delete it
  186. * - for each modified/extra we check those in with the comment/user/date
  187. * from the log message.
  188. */
  189. int
  190. cset(int rev, int verify, string url)
  191. {
  192. FILE f;
  193. string buf, tmp, svn, bk;
  194.  
  195. fprintf(stderr, "=== SVN ${rev} ===\n");
  196. unless (system("svn update -q -r${rev}") == 0) return (1);
  197. tmp = "BitKeeper/tmp/comments";
  198. f = fopen(tmp, "w");
  199. foreach (buf in log{rev}.cmts) {
  200. fprintf(f, "%s\n", buf);
  201. }
  202. fclose(f);
  203. setenv("BK_USER", log{rev}.user);
  204. setenv("BK_DATE_TIME_ZONE", log{rev}.date);
  205. system("bk -U^G rm -f");
  206. system("bk -xcU | bk ci -a${q}lY${tmp} -");
  207. f = fopen(tmp, "a");
  208. fprintf(f, "SVN: %d\n", rev);
  209. fclose(f);
  210. system("bk commit -${q}Y${tmp}");
  211. unless (verify) return (0);
  212.  
  213. svn = sprintf("/tmp/svn.%d", getpid());
  214. system("/bin/rm -rf ${svn}");
  215. unless (system("svn co -q -r${rev} ${url} ${svn}") == 0) {
  216. die("unable to check out ${rev} in ${svn}");
  217. }
  218. bk = sprintf("/tmp/bk.%d", getpid());
  219. system("/bin/rm -rf ${bk}");
  220. unless (system("bk export -r+ -tplain . ${bk}") == 0) {
  221. die("unable to export to ${bk}");
  222. }
  223.  
  224. buf = `bk diff -r --exclude=.svn ${svn} ${bk}`;
  225. // system("/bin/rm -rf ${svn} ${bk}");
  226. if (length(buf)) die(buf);
  227. }
  228.  
  229. /*
  230. * Load up the log, we'll use it for our commits.
  231. * ------------------------------------------------------------------------
  232. * r59 | mcccol | 2007-04-17 18:23:39 -0700 (Tue, 17 Apr 2007) | 4 lines
  233. *
  234. * removed logging, started using Debug.error
  235. *
  236. * ------------------------------------------------------------------------
  237. * r60 | mcccol | 2007-04-17 18:25:08 -0700 (Tue, 17 Apr 2007) | 4 lines
  238. *
  239. * * Added fixbad to utf8 to repair damaged utf8
  240. * * made regexps variables to preserver their regexp intrep
  241. *
  242. * etc.
  243. */
  244. int
  245. getlog(int incremental, int start, int stop)
  246. {
  247. FILE f;
  248. int rev;
  249. string cmts[];
  250. string buf;
  251. string last_date = "";
  252.  
  253. if (incremental) {
  254. start = (int)`svn info | grep Revision: | awk '{print $NF}'`;
  255. start++;
  256. }
  257. if (stop) {
  258. if (stop <= start) {
  259. fprintf(stderr, "Already up to or past %d\n", stop);
  260. exit(1);
  261. }
  262. f = popen("svn log -r${start}:${stop} 2>@stderr", "r");
  263. } else {
  264. f = popen("svn log -r${start}:HEAD 2>@stderr", "r");
  265. }
  266. unless ((buf = <f>) && (buf =~ /^[-]+$/)) {
  267. done: fprintf(stderr, "Seems like you are up to date.\n");
  268. return (0);
  269. }
  270.  
  271. while (!eof(f)) {
  272. unless (buf = <f>) {
  273. assert(eof(f));
  274. break;
  275. }
  276. if (buf =~ /\(no author\)/) buf =~ s/.no author./anon/;
  277. if (buf =~ /\(no date\)/) buf =~ s/.no date./${last_date}/;
  278. unless (buf =~ /^r(\d+) \| ([^|]+) \| ([0-9 :\-+]+) /) {
  279. die("expected rev/date: ${buf}\n");
  280. }
  281. rev = (int)$1;
  282. push(&revs, rev);
  283. log{rev}.user = (string)$2;
  284. last_date = log{rev}.date = (string)$3;
  285. buf = <f>; // toss the blank line
  286. undef(cmts); // toss previous comments
  287. while (buf = <f>) {
  288. if ((length(buf) == 72) && (buf =~ /^[-]+$/)) break;
  289. push(&cmts, buf);
  290. }
  291.  
  292. /*
  293. * Lose trailing blank lines, they serve no purpose.
  294. */
  295. while (defined(cmts[END]) && (cmts[END] =~ /^\s*$/)) {
  296. pop(&cmts);
  297. }
  298. /*
  299. for (i = length(cmts)-1; i >= 0; i--) {
  300. unless (cmts[i] =~ /^\s*$/) break;
  301. cmts[i] = undef;
  302. }
  303. */
  304. log{rev}.cmts = cmts;
  305. }
  306. pclose(f);
  307. unless (length(revs)) goto done;
  308. return (0);
  309. }
  310.  
  311. void
  312. usage(int which)
  313. {
  314. fprintf(stderr, "Barfed on %d.\n", which);
  315. exit(1);
  316. }
  317.