⬆️ ⬇️

Project Migration from StarTeam to SVN

Good day!



Who of you even heard of StarTeam? I think very few people, in the way like me a couple of months ago.



I had never heard of such a product from Borland before my current job. If you ask Google, it turns out that this product still exists and even develops, but as you all guessed, it will be far from the latest version and not even the penultimate one. I have version 5.3, which was developed around 2003, and installed and launched here in 2004. And almost 11 years she worked and solved their problems.



As an initiative specialist, my hair from this old monster immediately stirred and it was decided to migrate to something more modern, and this is either SVN or Git. The choice fell on SVN, because I have experience with it, as well as my colleague, and the management agreed with everything. The search for a ready-made solution led to Palarion Importer for SVN , there is even an article in Habré for this decision. But as it turned out, everything is not too simple, I have version 5.3, and this product assumes the existence of an SDK from the 2005 version, which turned out to be very problematic. Even using all sorts of tricks like renaming the necessary libraries, I could not launch this importer.

')

What else could you try? I tried to raise the new StarTeam server, because it has integration with SVN, and connect an existing server, but it didn't work out for me, because, apparently, they are not compatible with each other, the servers did not see each other.



And what did I do? Of course, I acted as a real programmer: I wrote my tool!



Design



So what's in the source data? I have:

  1. StarTeam Server 5.3
  2. StarTeam Client 5.3
  3. StarTeam SDK 5.3
  4. SVN server with a given repository
  5. TortoiseSVN with console client installed
  6. The project to be transferred




Meet StarTeam



I don’t know how it is with the current StarTeam, but the version that I have is terrible, because there is simply no such thing as a version - version. Each file is versioned separately, it is impossible to understand what was placed at one time with this file, as well as pull out the snapshot for some time. Well and everything else - this version can not always adequately understand which files are changed (that is, modified files are seen 100%, but those that did not touch 50/50 are understood to be modified).



Also, a problem has recently emerged with the fact that all comments and user names in Russian are displayed in the form of sets "?????", which means that the encoding has flown somewhere (and what is even more interesting is the encoding is not indicated anywhere in the client ). And why nobody happened to know exactly this, and when such a problem began, too, they cannot say for sure.



What else is StarTeam, you ask? From the database, which is based on MS SQL 2008. It would seem that you can write a competent request and pull out all that is required, right? But after seeing about 100 tables with names like S01, S02 and the View set for them, it was decided not even to deal with this all, because the encoding in the direct query in the database is not displayed correctly.



Action planning



So, we have the source project in an interesting repository and the final repository, which is empty. The project from Palarion requires starteam80.jar, which is included in the SDK, but I only have starteam53.jar in the SDK. I will say right away that I did not even program in Java before the start of this project, not a single line of code. But here it is necessary, then install the IDE and try. As an IDE, I chose NetBeans and began to parse what's inside starteam53.jar.

Inside this package is a whole set of classes that allows you to work with the StarTeam server. The Borland website has documentation for the SDK, which saves a lot of classes from meaningless hesitation. Next, I try to make a simple project that could connect to the server and return the list of projects. After 2 hours of torment with the libraries from StarTeam, I achieved the desired result. Now it becomes clear that it is possible to work with StarTeam through the SDK, so you only need to determine how to transfer the whole story.



But! How now to transfer the history with all the problems StarTeam?



StarTeam has a timestamp for changing each file, i.e. The history of changes to one single file with comments and the exact time of change can be obtained, which means we will build on it.



When a commit is made in StarTeam, a comment is written to each file that goes during a commit, so it’s enough to get a comment and an author from the first file, and the rest will match.



StarTeam has a file storage feature, it stores them in a project, there are files and folders in the project, and there may be more files and folders in the folder and each file has versioning.



And also, if you ask the SDK to just extract the file, StarTeam will extract the files to where it is configured by default in the client, which didn’t work for me and you need to first extract to the file stream (well, that is the possibility), and then save to disk.



I came up with the following migration path:

  1. Download everything from StarTeam to folders on a local disk;
  2. Make a passage through the folders and upload everything to SVN, in the comment field enter the time when the commit was made in StarTeam.


We will unload according to the following scheme:

  1. We begin recursive traversal of the folder tree and project files;
  2. Get the first file;
  3. We extract all its history;
  4. We make a walk through the history and put the files into a folder for exporting the project;
  5. Create a folder with a timestamp on the mask "yyyy.MM.dd.HH.mm" (I first did it with millisecond accuracy, but as it turned out, both files from the same commit can get into different revisions, which is wrong, and with this there were no conflicts and problems);
  6. In the folder we make a History file, where we write the name of the author, a comment and a timestamp for convenience;
  7. Do it until the last file is extracted.




Extract from StarTeam



So, NetBeans, Java and 0 experience in this programming language. The lack of development experience in Java did not frighten me, because there is Google, a one-time project and no one requires the highest knowledge from me, then somewhere you can simply sacrifice performance / memory / beauty of the code or all at once, because you just have to do it.



When extracting commits, I encountered the problem that the authors of the commit return as an ID, not as a string, which surprised me. A search on the documentation showed that authors can be obtained, but in the form of a list of ID + Name, but I have problems with the encoding and I cannot read a number of users, and for some reason there are a number of duplicates, and there were no duplicates in SVN. I found the required view in the database and from there by e-mail and by the method of exclusion I established to which author which ID belongs. Here is the biggest crutch: I wrote the return of the author's name, as well as the password and users through a switch-case, but it’s stupid and suboptimal, but for my problems, there’s no way to go.



Despite the lack of experience in working with this programming language, I did not intend to write bad code and it turned out like this:



StarTeam data extraction source code
Server StarTeamServer = new Server("WINAPPSRV", 49201); StarTeamServer.connect(); if (StarTeamServer.isConnected()) { System.out.println("Connect to server OK!"); StarTeamServer.logOn("markov", "123456"); if (StarTeamServer.isLoggedOn()) { System.out.println("LogOn to server OK!"); Project[] projects = StarTeamServer.getProjects(); Project TW = null; for (Project currentproject : projects) { if (currentproject.getName().equals("Tw")) { TW = currentproject; break; } } if (TW != null) { System.out.println("Try to find first revision"); View CurrentView = TW.getDefaultView(); //       /   ExtractFullTreeFromRoot(CurrentView.getRootFolder(), "/", "C:/StarTeamToSVN"); } else { System.out.println("Project Tw not found in StarTeam repository"); } } else { System.out.println("LogOn to server failed :'("); } StarTeamServer.disconnect(); 






Source Code Functions
 private static void ExtractFileHistory(com.starbase.starteam.File SourceFile, String SourceFolderName, String RootFolder) { Item[] FileHistory = SourceFile.getHistory(); for (Item CurrentHistoryItem : FileHistory) { com.starbase.starteam.File CurrentHistoryFile = (File) CurrentHistoryItem; String FullFileName = RootFolder + "/" + FormatOLEDATEToString(CurrentHistoryFile.getModifiedTime()) + "/Files" + SourceFolderName + CurrentHistoryFile.getName(); String FullPath = RootFolder + "/" + FormatOLEDATEToString(CurrentHistoryFile.getModifiedTime()) + "/Files" + SourceFolderName; String HistoryFileName = RootFolder + "/" + FormatOLEDATEToString(CurrentHistoryFile.getModifiedTime()) +"/@History.txt"; System.out.format("FileName = %s; Revision = %d; CreatedTime = %s; Author = %s; Comment = '%s';%n", FullFileName, CurrentHistoryFile.getRevisionNumber() + 1, FormatOLEDATEToString(CurrentHistoryFile.getModifiedTime()), FindAuthorNameById(CurrentHistoryFile.getModifiedBy()), CurrentHistoryFile.getComment()); FileOutputStream fop = null; java.io.File file; try { java.io.File directory = new java.io.File(FullPath); if (!directory.exists()) { directory.mkdirs(); } file = new java.io.File(FullFileName); fop = new FileOutputStream(file); CurrentHistoryFile.checkoutToStream(fop, com.starbase.starteam.Item.LockType.UNCHANGED, false); fop.flush(); fop.close(); java.io.File HistoryFile = new java.io.File(HistoryFileName); if (!HistoryFile.exists()) { PrintWriter out = new PrintWriter(HistoryFileName); out.println("AuthorID: " + CurrentHistoryFile.getModifiedBy()); out.println("AuthorName: " + FindAuthorNameById(CurrentHistoryFile.getModifiedBy())); out.println("TimeStamp: " + FormatOLEDATEToString(CurrentHistoryFile.getModifiedTime())); out.println("Comment: " + CurrentHistoryFile.getComment()); out.close(); } //System.out.println("Done"); } catch (IOException e) { e.printStackTrace(); } finally { try { if (fop != null) { fop.close(); } } catch (IOException e) { e.printStackTrace(); } } } } private static void ExtractFullTreeFromRoot(com.starbase.starteam.Folder SourceFolder, String SourceFolderName, String RootFolder) { ExtractFilesFromFolder(SourceFolder, SourceFolderName, RootFolder); Item[] RootFolders = SourceFolder.getItems("Folder"); for (Item CurrentItem : RootFolders) { Folder CurrentFolder = (Folder)CurrentItem; ExtractFullTreeFromRoot(CurrentFolder, SourceFolderName+CurrentFolder.getPathFragment()+"/", RootFolder); } } private static void ExtractFilesFromFolder(com.starbase.starteam.Folder SourceFolder, String SourceFolderName, String RootFolder) { Item[] RootFiles = SourceFolder.getItems("File"); for (Item CurrentItem : RootFiles) { com.starbase.starteam.File MyFile = (File) CurrentItem; if (SourceFolderName.isEmpty()) { ExtractFileHistory(MyFile, "/", RootFolder); } else { ExtractFileHistory(MyFile, SourceFolderName, RootFolder); } } } private static String FormatOLEDATEToString(OLEDate SourceValue) { DateFormat formatter = new SimpleDateFormat("yyyy.MM.dd.HH.mm"); return formatter.format(SourceValue.createDate()); } 




Even completely not bad happened, in my opinion.



According to this extraction scheme, I got almost 2000 revisions, which is not enough for a project that lives so long, but this is due to the development features adopted here and the software itself. And also, for a long time, I manually checked whether the revisions that I extracted were correct and correctly decomposed.



Now it remains to commit everything correctly in SVN, and this is a little easier than the first stage.



Fill in SVN



The fill pattern in SVN is simple:

1) We get a complete list of folders and sort it (naming folders helps us with this);

2) Create a list of missing files and folders in the SVN folder for the current revision;

3) Copy the contents of the Files subfolder to the SVN folder;

4) For missing files and folders in SVN execute add;

5) Make a commit of the current working copy of SVN.



During the implementation, I ran into a number of problems. Namely, in the comments to the commit, you need to put a backslash before double quotes so that the quotes are processed correctly. And when adding a file or path containing the @ symbol, you need to add one more @ at the end in order for svn to correctly understand the file name. Here the code was worse, because I wanted to quickly.



Spoiler header
  //1.    - java.io.File dir = new java.io.File("C:/StarTeamToSVN"); java.io.File[] subDirs = dir.listFiles(new FileFilter() { @Override public boolean accept(java.io.File pathname) { return pathname.isDirectory(); } }); //2.   Arrays.sort(subDirs); //3.         //1.      ,    //2.         SVN //3.   Files        StarTeam  SVN //4.       SVN  //5.        java.io.File RootSVN = new java.io.File("C:\\TestASU"); for (java.io.File CurrentDir : subDirs){ try { ArrayList<java.io.File> MyFiles = new ArrayList<>(); String StarTeamSourceFolder = CurrentDir.getAbsolutePath()+"\\Files\\"; listf(StarTeamSourceFolder, MyFiles); ///String[] SVNAddFiles = new String[](); ArrayList<String> SVNAddFiles = new ArrayList<>(); for (java.io.File CurrentFile: MyFiles) { String FullSourcePath = CurrentFile.getAbsolutePath(); String FullDestPath = "C:\\TestASU\\" + FullSourcePath.substring(FullSourcePath.indexOf(StarTeamSourceFolder) + StarTeamSourceFolder.length()) ; //   ,     java.io.File DestFile = new java.io.File(FullDestPath); java.io.File ParentFolder = DestFile.getParentFile(); while ((ParentFolder != null) && (ParentFolder.compareTo(RootSVN) != 0 )) { if (!ParentFolder.exists()) { SVNAddFiles.add(0, ParentFolder.getAbsolutePath()); } ParentFolder = ParentFolder.getParentFile(); } if (!DestFile.exists()) { SVNAddFiles.add(FullDestPath); } } //       Set<String> s = new LinkedHashSet<>(SVNAddFiles); //  java.io.File RootStarTeam = new java.io.File(StarTeamSourceFolder); try { copyFolder(RootStarTeam, RootSVN); } catch (IOException ex) { Logger.getLogger(StarTeamToSVN.class.getName()).log(Level.SEVERE, null, ex); } Thread.sleep(5000); //  / for (String NewItem: s) { SVNAddFile(NewItem, CurrentDir.getName()); } //    String HistoryPath = CurrentDir.getAbsolutePath() + "\\@History.txt"; String AuthorUserName = ""; String AuthorPassword = ""; String AuthorComment = ""; try { for (String line : Files.readAllLines(Paths.get(HistoryPath), Charset.defaultCharset())) { if (line.contains("AuthorID")) { String AuthorID = line.substring(line.indexOf(": ")+2); AuthorUserName = FindAuthorUserNameByID(Integer.parseInt(AuthorID)); AuthorPassword = FindAuthorPasswordNameByID(Integer.parseInt(AuthorID)); } if (line.contains("TimeStamp")) { AuthorComment = line.substring(line.indexOf(": ")+2); } if (line.contains("Comment")) { AuthorComment = AuthorComment + "\n" + line.substring(line.indexOf(": ")+2); } if (!line.contains(": ")) { AuthorComment = AuthorComment + "\n" + line; } } } catch (IOException ex) { Logger.getLogger(StarTeamToSVN.class.getName()).log(Level.SEVERE, null, ex); } if ((AuthorUserName == "")||(AuthorPassword == "")) { throw new IOException("Ho Authentification data"); } Thread.sleep(1000); SVNCommit("C:\\TestASU\\", CurrentDir.getName(), AuthorUserName, AuthorPassword, AuthorComment); } catch (InterruptedException ex) { Logger.getLogger(StarTeamToSVN.class.getName()).log(Level.SEVERE, null, ex); } } 




There are temporary delays, because svn does not always have time to add everything to its database, and sometimes the files are interesting for the virus, the selection method turned out that with such values ​​there are no problems, but I also just have a slow hard drive, so I also have problems. In svn, if you execute svn add FOLDERNAME, the entire folder with the contents will be added, but it seemed to me a more correct way to add folders and files one by one in order to control the process more precisely.



Conclusion



While I was writing this article, my project completely migrated. I know that it is not uncommon for IT specialists to get to where old technologies are used and they would like to switch to something more modern, so I posted my project on github. Here is a link to the project .



I also got a Java programming experience. The code should be refined for a specific project, but it fully works and requires a minimum of modifications. It also allows migrating to those who have different versions of the SDK, or adjusting my code to a specific version of the SDK.



I hope that this article will be useful to someone in the future.

Source: https://habr.com/ru/post/248239/



All Articles