Where I work, we use CVS for source code control. Some time ago, I was on a project with 4 other developers. We were at this code review meeting where I wanted to review the source code written by one of the developers in the team.
The developer had downloaded the source from CVS and done some changes in his local copy without committing the changes back into CVS. I wanted to review the changes locally before letting him commit. And while we were in this meeting, I copied the folder that had the source code on his machine, on my computer, and walked through the code with the other developers during the meeting. I made some changes here and there, and then after the meeting, probably forgot all about it.
A few days later, I forgot that the source code I was working on, on my computer was the one I copied from the other developer's machine. Oblivious of this recent happening, I went on to use the same source code base and made more changes and then tried checking-in my source code.
Obviously, CVS did not let me check in that code because it had come from someone else's machine, the other developer that had gotten the latest version.
When you get the latest version of a code base from CVS, or you check-out the latest version, either ways, CVS creates a new file called Root, which it places in your machine, in the root folder of your code repository and in every subfolder underneath. So, if you checked out, or got the latest version of source code under the folder A, and the folder hierarchy was such that this folder A contained some files and also folders B and C, and each of those contained more files, but folder C contained folder D, then CVS would create a file called Root under each of the folders A, B, C and D on your local machine.
The contents of this file called Root would be a string that represented your credentials for logging in into the CVS repository. Therefore, if your name is Big Joe, and your login name on the CVS server was bigjoe, then the contents of the Root file in each of the folders A, B, C and D on your machine would look something like:
:ssh:[email protected]
If you made changes to the code and then tried checking in the code, CVS would open and read each of these files named Root, to see if it was actually you who had first downloaded the code you are trying to check in.
So, this file in effect identifies the person who downloaded the code, a modified version probably, of which, is being checked-in. If the Root file does not have your credentials, it will not let you check in that code.
So, that day, when I tried checking in the code, I couldn't, because all the Root files in all the folders and subfolders of this mega huge project with over a thousand files had the other developer's signatures.
Therefore, I thought about it and realized that I could either manually go and change those 100 odd files or write a simple script to do the same that everyone else who met with the same problem could use.
So, this script below will help you check-in your code in case you got the code from another developer who originally downloaded it from CVS.
To run this script on the command line, use the syntax given below.
C:\> fix FolderPath LookFor ReplaceWith [FileNameFilter]
Where
FolderPath = The path of the root folder within which you wish to change the Root files. This is the same as the root folder for all your code that you are trying to check-in.
LookFor = The string you wish to look for. This should be the credentials of the guy you wish to replace with your own. So, if I wanted to replace Big Joe's credentials with my own, I'd type in the value ':ssh:bigjoe@' for the LookFor command line parameter. Optionally, I could even use a regular expression as a value for this parameter. So, if all CVS login names were 6 characters in length, and I wanted to change multiple Root files that came from multiple developers, I could type ':ssh:\w{6}@' or ':ssh:[A-Za-z0-9]{6}@' for this parameter.
ReplaceWith = The string you wish to replace the value you specified in the LookFor parameter with. In this case, it will be your own CVS credentials.
FileNameFilter = This is an optional parameter that you can use to make changes to any other file other than the CVS generated Root file that we've talked about. You can supply wild cards in this expression, too. So, if you supplied an asterisk (*) for this parameter, all files in the folder will be checked for the string specified in the LookFor parameter and replaced with the value specified in the ReplaceWith parameter. On the other hand, if you leave this parameter unspecified, only the CVS generated files with the name Root will be scanned and edited.
I'm calling it fix.exe but you could change that and call it anything else. I like simple and short names on the command line.
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
namespace Fix
{
class FixIt
{
private const string ROOT = "Root";
static void Main(string[] args)
{
if (args.Length < 3)
{
StringBuilder sMessage = new StringBuilder("\nError: Invalid argument\n\nCorrect Usage:\n--------------\n\n\tfix FolderPath LookFor ReplaceWith [FileNameFilter]\n\n\n");
sMessage.Append("NOTES:\n-------\n\nFileNameFilter: Is optional. Can be a valid wildcard expression. If left out, all files with the name 'Root' shall be scanned.\n\n");
sMessage.Append("LookFor: Can be a valid regular expression.\n\n");
sMessage.Append("EXAMPLES:\n\tfix \"C:\\My Project\" \"poo\" \"poop\" \"*\"\n");
sMessage.Append("\tfix . \":ssh:[a-z]{7}\" \":ssh:schakra\"\n\n\n\n");
Console.WriteLine(sMessage);
return;
}
try
{
Console.WriteLine("{0}\n\n", new FixIt().ReplaceText(args[0], (args.Length > 3)? args[3]: ROOT, args[1], args[2]));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return;
}
}
public string ReplaceText(string root, string fileFilter,string lookForWhat, string replaceWith)
{
string[] files = System.IO.Directory.GetFiles(root, fileFilter, System.IO.SearchOption.AllDirectories);
if (files.Length > 0)
foreach (string file in files)
ReplaceInFile(file, lookForWhat, replaceWith);
return string.Format("{0} files affected.", files.Length);
}
private void ReplaceInFile(string filePath, string searchText, string replaceText)
{
StreamReader reader = null;
StreamWriter writer = null;
try
{
reader = new StreamReader(filePath);
string content = reader.ReadToEnd();
reader.Close();
content = Regex.Replace(content, searchText, replaceText);
writer = new StreamWriter(filePath);
writer.Write(content);
}
finally
{
if (reader != null) reader.Close();
if (writer != null) writer.Close();
}
}
}
}