svn2cvs.pp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. {$mode objfpc}
  2. {$H+}
  3. program svn2cvs;
  4. uses Classes,sysutils,process,DOM,xmlread,custapp,IniFiles;
  5. Const
  6. SGlobal = 'Global';
  7. KeyCVSBin = 'CVSBinary';
  8. KeySVNBin = 'SVNBinary';
  9. KeySVNURL = 'SVNURL';
  10. KeyCVSROOT = 'CVSROOT';
  11. KeyRepository = 'CVSRepository';
  12. KeyRevision = 'Revision';
  13. KeyWorkDir = 'WorkingDir';
  14. Resourcestring
  15. SErrFailedToCheckOut = 'Failed to check out SVN repository';
  16. SErrFailedToInitCVS = 'Failed to initialize CVS: ';
  17. SErrNoRepository = 'Cannot initialize CVS: no CVS Repository specified';
  18. SErrDirectoryFailed = 'Failed to create directory : %s';
  19. SErrFailedToGetVersions = 'Failed to retrieve SVN versions';
  20. SErrInValidSVNLog = 'Invalid SVN log.';
  21. SErrUpdateFailed = 'Update to revision %d failed.';
  22. SErrFailedToCommit = 'Failed to commit to CVS.';
  23. SErrFailedToRemove = 'Failed to remove file: %s';
  24. SErrFailedToAddDirectory = 'Failed to add directory to CVS: %s';
  25. SErrFailedToAddFile = 'Failed to add file to CVS: %s';
  26. SErrDirectoryNotInCVS = 'Directory not in CVS: %s';
  27. SLogRevision = 'Revision %s by %s :';
  28. SConvertingRevision = 'Converting revision : %d';
  29. SWarnUnknownAction = 'Warning: Unknown action: "%s" for filename : "%s"';
  30. SWarnErrorInLine = 'Warning: Erroneous file line : %s';
  31. SExecuting = 'Executing: %s';
  32. Type
  33. { TSVN2CVSApp }
  34. TVersion = Class(TCollectionItem)
  35. private
  36. FAuthor: String;
  37. FDate: string;
  38. FLogMessage: String;
  39. FRevision: Integer;
  40. Public
  41. Property Revision : Integer read FRevision;
  42. Property LogMessage : String Read FLogMessage;
  43. Property Date : string Read FDate;
  44. Property Author : String Read FAuthor;
  45. end;
  46. { TVersions }
  47. TVersions = Class(TCollection)
  48. private
  49. function GetVersion(Index : INteger): TVersion;
  50. procedure SetVersion(Index : INteger; const AValue: TVersion);
  51. Protected
  52. procedure ConvertLogEntry(E : TDomElement);
  53. public
  54. Procedure LoadFromXML(Doc : TXMlDocument);
  55. property Versions[Index : INteger] : TVersion Read GetVersion Write SetVersion; Default;
  56. end;
  57. { TSVN2CVSApp }
  58. TSVN2CVSApp = Class(TCustomApplication)
  59. Public
  60. SVNBin : String;
  61. CVSBin : String;
  62. versions : TVersions;
  63. WorkingDir : String;
  64. StartRevision : Integer;
  65. SVNURL : String;
  66. CVSROOT : String;
  67. CVSRepository : String;
  68. Function RunCmd(Cmd: String; CmdOutput: TStream): Boolean;
  69. Function RunSVN(Cmd : String; CmdOutput : TStream) : Boolean;
  70. Function RunCVS(Cmd : String; CmdOutput : TStream) : Boolean;
  71. Function UpdateSVN(Version : TVersion; Files : TStrings) : Boolean;
  72. Procedure WriteLogMessage(Version : TVersion);
  73. Procedure UpdateEntry(AFileName : String);
  74. Procedure DeleteEntry(AFileName : String);
  75. Procedure DoCVSEntries(Version : TVersion;Files : TStrings);
  76. procedure CheckInCVS;
  77. procedure CheckOutSVN(Files : TStrings);
  78. Procedure ConvertVersion(Version : TVersion);
  79. Procedure ConvertRepository;
  80. procedure GetVersions;
  81. procedure ProcessConfigFile;
  82. Function ProcessArguments : Boolean;
  83. Procedure DoRun; override;
  84. end;
  85. AppError = Class(Exception);
  86. { TVersions }
  87. function TVersions.GetVersion(Index : INteger): TVersion;
  88. begin
  89. Result:=Items[Index] as Tversion;
  90. end;
  91. procedure TVersions.SetVersion(Index : INteger; const AValue: TVersion);
  92. begin
  93. Items[Index]:=AValue;
  94. end;
  95. procedure TVersions.ConvertLogEntry(E : TDomElement);
  96. Function GetNodeText(N : TDomNode) : String;
  97. begin
  98. N:=N.FirstChild;
  99. If N<>Nil then
  100. Result:=N.NodeValue;
  101. end;
  102. Var
  103. N : TDomNode;
  104. V : TVersion;
  105. begin
  106. V:=Add as TVersion;
  107. V.FRevision:=StrToIntDef(E['revision'],-1);
  108. N:=E.FirstChild;
  109. While (N<>Nil) do
  110. begin
  111. If (N.NodeType=ELEMENT_NODE) then
  112. begin
  113. if (N.NodeName='author') then
  114. V.FAuthor:=GetNodeText(N)
  115. else If (N.NodeName='date') then
  116. V.FDate:=GetNodeText(N)
  117. else If (N.NodeName='msg') then
  118. V.FLogMessage:=GetNodeText(N);
  119. end;
  120. N:=N.NextSibling;
  121. end;
  122. end;
  123. procedure TVersions.LoadFromXML(Doc: TXMlDocument);
  124. var
  125. L : TDomNode;
  126. E : TDomElement;
  127. begin
  128. L:=Doc.FirstChild;
  129. While (L<>Nil) and not ((L.NodeType=ELEMENT_NODE) and (L.NodeName='log')) do
  130. L:=L.NextSibling;
  131. if (L=Nil) then
  132. Raise AppError.Create(SErrInValidSVNLog);
  133. L:=L.FirstChild;
  134. While (L<>Nil) do
  135. begin
  136. If (L.NodeType=ELEMENT_NODE) and (L.NodeName='logentry') then
  137. E:=TDomElement(L);
  138. ConvertLogEntry(E);
  139. L:=L.NextSibling;
  140. end;
  141. end;
  142. { TSVN2CVSApp }
  143. function TSVN2CVSApp.RunCmd(Cmd: String; CmdOutput: TStream): Boolean;
  144. Var
  145. Buf : Array[1..4096] of Byte;
  146. Count : Integer;
  147. begin
  148. With TProcess.Create(Self) do
  149. Try
  150. CommandLine:=cmd;
  151. Writeln(Format(SExecuting,[CommandLine]));
  152. if (CmdOutput<>Nil) then
  153. Options:=[poUsePipes];
  154. Execute;
  155. If (CmdOutPut=Nil) then
  156. WaitOnExit
  157. else
  158. Repeat
  159. Count:=Output.Read(Buf,SizeOf(Buf));
  160. If (Count>0) then
  161. cmdOutput.Write(Buf,Count);
  162. Until (Count=0);
  163. Result:=(ExitStatus=0);
  164. finally
  165. Free;
  166. end;
  167. end;
  168. function TSVN2CVSApp.RunSVN(Cmd: String; CmdOutput: TStream): Boolean;
  169. begin
  170. Result:=RunCmd(SVNbin+' '+Cmd,CmdOutput);
  171. end;
  172. function TSVN2CVSApp.RunCVS(Cmd: String; CmdOutput: TStream): Boolean;
  173. begin
  174. Result:=RunCmd(CVSbin+' '+Cmd,CmdOutput);
  175. end;
  176. procedure TSVN2CVSApp.CheckOutSVN(Files : TStrings);
  177. Var
  178. S : TStringStream;
  179. begin
  180. S:=TStringStream.Create('');
  181. Try
  182. if not RunSVN(Format('co -r %d %s .',[StartRevision,SVNURL]),S) then
  183. Raise AppError.Create(SErrFailedToCheckOut);
  184. Files.Text:=S.DataString;
  185. Finally
  186. FreeAndNil(S);
  187. end;
  188. end;
  189. procedure TSVN2CVSApp.CheckInCVS;
  190. Var
  191. F : Text;
  192. begin
  193. If not ForceDirectories(WorkingDir+'CVS') then
  194. Try
  195. AssignFile(F,WorkingDir+'CVS/Root');
  196. Rewrite(F);
  197. Try
  198. Writeln(F,CVSRoot);
  199. Finally
  200. CloseFile(F);
  201. end;
  202. AssignFile(F,WorkingDir+'CVS/Repository');
  203. Rewrite(F);
  204. Try
  205. Writeln(F,CVSRepository);
  206. Finally
  207. Close(F);
  208. end;
  209. AssignFile(F,WorkingDir+'CVS/Entries');
  210. Rewrite(F);
  211. Try
  212. // Do nothing.
  213. Finally
  214. Close(F);
  215. end;
  216. except
  217. On E : Exception do
  218. begin
  219. E.Message:=SErrFailedToInitCVS+E.Message;
  220. Raise;
  221. end;
  222. end;
  223. end;
  224. procedure TSVN2CVSApp.Convertrepository;
  225. Var
  226. InitCVS,INITSVN : Boolean;
  227. I : Integer;
  228. Files : TStringList;
  229. begin
  230. If Not DirectoryExists(WorkingDir) then
  231. begin
  232. if Not ForceDirectories(WorkingDir) then
  233. Raise AppError.CreateFmt(SErrDirectoryFailed,[WorkingDir]);
  234. InitSVN:=True;
  235. InitCVS:=true;
  236. end
  237. else
  238. begin
  239. if Not DirectoryExists(WorkingDir+'.svn') then
  240. InitSVN:=True;
  241. if Not DirectoryExists(WorkingDir+'CVS') then
  242. InitCVS:=True;
  243. end;
  244. ChDir(WorkingDir);
  245. if InitCVS and (CVSRepository='') then
  246. Raise AppError.Create(SErrNoRepository);
  247. if InitSVN then
  248. begin
  249. Files:=TStringList.Create;
  250. Try
  251. CheckoutSVN(Files);
  252. if InitCVS then
  253. begin
  254. CheckinCVS;
  255. DoCVSEntries(Nil,Files);
  256. end
  257. else
  258. DoCVSEntries(Nil,Files);
  259. finally
  260. FreeAndNil(Files);
  261. end;
  262. end;
  263. GetVersions;
  264. For I:=0 to Versions.Count-1 do
  265. ConvertVersion(Versions[i]);
  266. end;
  267. procedure TSVN2CVSApp.GetVersions;
  268. Var
  269. S : TStringStream;
  270. Doc : TXMLDocument;
  271. begin
  272. Versions:=TVersions.Create(TVersion);
  273. S:=TStringStream.Create('');
  274. Try
  275. if not RunSVN(Format('log --xml -r %d:HEAD',[StartRevision]),S) then
  276. Raise AppError(SErrFailedToGetVersions);
  277. S.Position:=0;
  278. ReadXMLFile(Doc,S);
  279. Try
  280. Versions.LoadFromXML(Doc);
  281. finally
  282. Doc.Free;
  283. end;
  284. Finally
  285. S.Free;
  286. end;
  287. end;
  288. procedure TSVN2CVSApp.ConvertVersion(Version: TVersion);
  289. Var
  290. Files : TStringList;
  291. begin
  292. Writeln(Format(SConvertingRevision,[Version.revision]));
  293. Files:=TStringList.Create;
  294. Try
  295. If Not UpdateSVN(Version,Files) then
  296. Raise AppError.CreateFmt(SErrUpdateFailed,[Version.Revision]);
  297. DoCVSEntries(Version,Files);
  298. Finally
  299. Files.Free;
  300. end;
  301. end;
  302. Function TSVN2CVSApp.UpdateSVN(Version : TVersion; Files : TStrings) : Boolean;
  303. Var
  304. S : TStringStream;
  305. begin
  306. S:=TStringStream.Create('');
  307. Try
  308. Result:=RunSVN(Format('up -r %d',[version.revision]),S);
  309. if Result then
  310. Files.Text:=S.DataString;
  311. Finally
  312. S.Free;
  313. end;
  314. end;
  315. Procedure TSVN2CVSApp.WriteLogMessage(Version : TVersion);
  316. Var
  317. F : Text;
  318. begin
  319. AssignFile(F,'logmsg.txt');
  320. Rewrite(F);
  321. Try
  322. Writeln(F,Format(SLogRevision,[Version.Revision,Version.Author]));
  323. Writeln(F, Version.LogMessage);
  324. Finally
  325. CloseFile(F);
  326. end;
  327. end;
  328. Procedure TSVN2CVSApp.DoCVSEntries(Version : TVersion;Files : TStrings);
  329. Var
  330. I,P : Integer;
  331. Action : Char;
  332. FileName : String;
  333. begin
  334. For I:=0 to Files.Count-1 do
  335. begin
  336. FileName:=trim(Files[i]);
  337. P:=Pos(' ',FileName);
  338. if (P=0) then
  339. Writeln(StdErr,Format(SWarnErrorInLine,[FileName]))
  340. else
  341. begin
  342. Action:=FileName[1];
  343. system.Delete(FileName,1,P);
  344. FileName:=Trim(FileName);
  345. end;
  346. Case UpCase(action) of
  347. 'U' : UpdateEntry(FileName);
  348. 'D' : DeleteEntry(FileName);
  349. else
  350. Writeln(stdErr,Format(SWarnUnknownAction,[Action,FileName]));
  351. end;
  352. end;
  353. WriteLogMessage(version);
  354. Try
  355. If not RunCVS('commit -m -F logmsg.txt .',Nil) then
  356. Raise AppError.Create(SErrFailedToCommit);
  357. Finally
  358. if not DeleteFile('logmsg.txt') then
  359. Writeln(StdErr,'Warning: failed to remove log message file.');
  360. end;
  361. end;
  362. Procedure TSVN2CVSApp.UpdateEntry(AFileName : String);
  363. Var
  364. FD : String;
  365. L : TStringList;
  366. I : Integer;
  367. Found : Boolean;
  368. begin
  369. If ((FileGetAttr(AFileName) and faDirectory)<>0) then
  370. begin
  371. if Not RunCVS('add '+AFileName,Nil) then
  372. Raise AppError.CreateFmt(SErrFailedToAddDirectory,[AFileName]);
  373. end
  374. else // Check if file is under CVS control by checking the Entries file.
  375. begin
  376. FD:=ExtractFilePath(AFileName);
  377. If not DirectoryExists(FD+'Entries') then
  378. Raise AppError.CreateFmt(SErrDirectoryNotInCVS,[FD]);
  379. Found:=False;
  380. L:=TStringList.Create;
  381. Try
  382. L.LoadFromFile(FD+'Entries');
  383. Found:=False;
  384. I:=0;
  385. While (not found) and (I<L.Count) do
  386. begin
  387. Inc(I);
  388. end;
  389. if not found then
  390. if Not RunCVS('add '+AFileName,Nil) then
  391. Raise AppError.CreateFmt(SErrFailedToAddFile,[AFileName]);
  392. finally
  393. L.Free;
  394. end;
  395. end;
  396. end;
  397. Procedure TSVN2CVSApp.DeleteEntry(AFileName : String);
  398. begin
  399. If ((FileGetAttr(AFileName) and faDirectory)=0) then
  400. if Not RunCVS('rm '+AFileName,Nil) then
  401. Raise AppError.CreateFmt(SErrFailedToRemove,[AFileName]);
  402. end;
  403. procedure TSVN2CVSApp.DoRun;
  404. begin
  405. If Not ProcessArguments then
  406. exit;
  407. ConvertRepository;
  408. end;
  409. procedure TSVN2CVSApp.ProcessConfigFile;
  410. begin
  411. With TMemIniFile.Create(GetAppConfigFile(False)) do
  412. try
  413. SVNURL:=ReadString(SGlobal,KeySVNURL,'');
  414. CVSROOT:=ReadString(SGlobal,KeyCVSROOT,'');
  415. CVSRepository:=ReadString(SGlobal,KeyRepository,'');
  416. WorkingDir:=ReadString(SGLobal,KeyWorkDir,'');
  417. StartRevision:=ReadInteger(SGlobal,KeyRevision,-1)+1;
  418. SVNBin:=ReadString(SGlobal,KeySVNBin,'svn');
  419. CVSBin:=ReadString(SGlobal,KeyCVSBin,'cvs');
  420. finally
  421. Free;
  422. end;
  423. end;
  424. function TSVN2CVSApp.ProcessArguments: Boolean;
  425. begin
  426. ProcessConfigFile;
  427. if HasOption('s','svn-repository') then
  428. SVNURL:=GetOptionValue('s','svn-repository');
  429. if HasOption('c','cvsroot') then
  430. CVSROOT:=GetOptionValue('c','cvsroot');
  431. if HasOption('c','cvsrepository') then
  432. CVSROOT:=GetOptionValue('p','cvsrepository');
  433. if HasOption('r','revision') then
  434. StartRevision:=StrToIntDef(GetOptionValue('c'),0);
  435. if HasOption('d','directory') then
  436. WorkingDir:=GetOptionValue('d','directory');
  437. Result:=(SVNUrl<>'') and (CVSROOT<>'');
  438. If Result then
  439. begin
  440. If (WorkingDir='') then
  441. WorkingDir:=GetCurrentDir;
  442. WorkingDir:=IncludeTrailingPathDelimiter(WorkingDir);
  443. end;
  444. end;
  445. begin
  446. With TSVN2CVSApp.Create(Nil) do
  447. try
  448. Initialize;
  449. Run;
  450. Finally
  451. free;
  452. end;
  453. end.