The class that encapsulates a description of a change.
Esempio n. 1
0
        /// <summary>
        /// Gets the change from the repository
        /// Returns null if any error occurs, or the change is not pending.
        /// </summary>
        /// <param name="changeId"> shelveset identifier. </param>
        /// <param name="includeBranchedFiles"> Include full text for branched and integrated files. </param>
        /// <returns> The change. </returns>
        public Change GetChange(string changeId, bool includeBranchedFiles)
        {
            if (!File.Exists(diffFromSvnBatFilename))
            {
                Console.WriteLine("difffromsvn.bat does not exist in the same directory as review.exe.");
                Console.WriteLine(" This batch file must run 'diff.exe %6 %7'");
                return null;
            }

            // first get the list of all files (including paths):
            // svn status --changelist xxx
            string result = RunClient(@"status --changelist """ + changeId + "\" \"" + localRoot + "\" ", false);

            StringReader sr = new StringReader(result);
            string line = null;

            bool seenDash = false;

            Regex fileAfterSpaceRegEx = new Regex(@"^(?<changetype>.).......(?<filename>(\S)+)$");

            List<ChangeFile> files = new List<ChangeFile>();

            // Results look like this:
            //--- Changelist 'mine':
            //        util\VisitorPrefsGetter.java
            //M       util\VisitorPrefsEidMigrator.java

            // Rules:
            // skip past first line that begins with "-"
            // then for each line, get the filename by finding the first non-whitespace after a white-space
            while ((line = sr.ReadLine()) != null)
            {
                if (!seenDash)
                {
                    if ((line.Length > 0) && (line[0] == '-'))
                    {
                        seenDash = true;
                    }
                    continue;
                }

                Match match = fileAfterSpaceRegEx.Match(line);
                if (!match.Success)
                {
                    Console.WriteLine("Could not interpret svn status output: " + line);
                    return null;
                }

                // what type of change is it?
                string typeOfChange = match.Groups[2].Value;
                if (typeOfChange.Length == 0)
                {
                    // no change, skip this
                    continue;
                }
                ChangeFile.SourceControlAction action = ChangeFile.SourceControlAction.EDIT;

                switch (typeOfChange)
                {
                    case "C":   // conflicted
                    case "M":   // modified
                    case "R":   // replaced
                    case " ":   // no change but added to the changelist
                        action = ChangeFile.SourceControlAction.EDIT;
                        break;
                    case "D":   // deleted
                    case "!":
                        action = ChangeFile.SourceControlAction.DELETE;
                        break;
                    case "I":   // ignored
                    case "A":   // added
                        action = ChangeFile.SourceControlAction.ADD;
                        break;
                    case "?":   // not under version control
                        break;
                    default:
                        // unexpected
                        Console.WriteLine("Unexpected change type: " + typeOfChange);
                        return null;
                }

                // this is the filename
                string filename = match.Groups[3].Value;

                bool isText = false;
                // get the mime-type to see if it's text
                string mimetype = RunClient("propget svn:mime-type " + "\"" + filename + "\"", false);
                if (mimetype == null || mimetype.StartsWith("text") || mimetype.Length == 0)
                {
                    isText = true;
                }

                // get the current revision
                string serverFilename = null;
                int revisionId = GetRevisionFromFile(filename, ref serverFilename);

                ChangeFile cf = new ChangeFile(serverFilename, action, revisionId, isText);
                cf.LocalFileName = filename;
                files.Add(cf);
            }

            if (!seenDash)
                return null;

            if (files.Count > 0)
            {
                // TODO: figure out name of client and description
                Change change = new Change(SourceControl, Client, changeId,
                    DateTime.Now.ToUniversalTime(), changeId, files.ToArray());

                FillInFileData(change);
                return change;
            }

            return null;
        }
Esempio n. 2
0
        /// <summary>
        /// Iterates through every file in the change list, and:
        ///     1. fills in its local path and 
        ///     2. diff if it is an edit, or the add file itself if it is an add.
        /// </summary>
        /// <param name="change"> The change list, instantiated. </param>
        private void FillInFileData(Change change)
        {
            foreach (ChangeFile file in change.Files)
            {
                if (File.Exists(file.LocalFileName))
                    file.LastModifiedTime = File.GetLastWriteTimeUtc(file.LocalFileName);

                if (!file.IsText)
                    continue;

                if (file.Action == ChangeFile.SourceControlAction.EDIT ||
                    (file.Action == ChangeFile.SourceControlAction.INTEGRATE))
                {
                    // The following depends on a diff command that will diff %6 and %7.
                    // Contents of the file is one line:
                    // @diff %6 %7
                    // This is something that the svn developers recommend (they choose not to fix this)
                    string result = RunClient("diff --diff-cmd \"" + diffFromSvnBatFilename + "\" \"" + file.LocalFileName + "\"",
                                                true);
                    // skip past first line
                    StringReader sr = new StringReader(result);
                    StringBuilder sb = new StringBuilder();
                    for (; ; )
                    {
                        string line = sr.ReadLine();
                        if (line == null)
                            break;

                        if (line.Equals(""))
                            continue;

                        if (line.StartsWith("="))
                            continue;

                        sb.Append(line);
                        sb.Append('\n');
                    }

                    file.Data = sb.ToString();
                    sr.Close();
                }
                else if (file.Action == ChangeFile.SourceControlAction.ADD ||
                    (file.Action == ChangeFile.SourceControlAction.BRANCH))
                {
                    try
                    {
                        StreamReader reader = new StreamReader(file.LocalFileName);
                        file.Data = reader.ReadToEnd();
                        reader.Close();
                    }
                    catch (FileNotFoundException)
                    {
                        Console.WriteLine("File not found: " + file.LocalFileName);
                        throw new SourceControlRuntimeError();
                    }
                }
            }
        }
Esempio n. 3
0
        /// <summary>
        /// For every file in the change list, fill in its local path and either diff if it is an edit, or the
        /// file itself if it is an add.
        /// </summary>
        /// <param name="change"> The change list, instantiated. </param>
        /// <param name="includeBranchedFiles"> Include the text for branched and integrated files. </param>
        private void FillInFileData(Change change, bool includeBranchedFiles)
        {
            foreach (ChangeFile file in change.Files)
            {
                string where = RunClient("where \"" + file.ServerFileName + "\"", false);
                if (where == null)
                    BugOut("sd|p4 where " + file.ServerFileName);

                Match files = ClientWhereParser.Match(where);
                if (!files.Success)
                    BugOut(where);

                file.LocalFileName = files.Groups[3].Value;
                if (File.Exists(file.LocalFileName))
                    file.LastModifiedTime = File.GetLastWriteTimeUtc(file.LocalFileName);

                if (!file.IsText)
                    continue;

                if (file.Action == ChangeFile.SourceControlAction.EDIT ||
                    (file.Action == ChangeFile.SourceControlAction.INTEGRATE && includeBranchedFiles))
                {
                    file.Data = RunClient("diff \"" + file.ServerFileName + "\"", true);
                }
                else if (file.Action == ChangeFile.SourceControlAction.ADD ||
                    (file.Action == ChangeFile.SourceControlAction.BRANCH && includeBranchedFiles))
                {
                    try
                    {
                        StreamReader reader = new StreamReader(file.LocalFileName);
                        file.Data = reader.ReadToEnd();
                        reader.Close();
                    }
                    catch (FileNotFoundException)
                    {
                        Console.WriteLine("File not found: " + file.LocalFileName);
                        throw new SourceControlRuntimeError();
                    }
                }
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Gets the change from the source control system. The change must be pending.
        /// Returns null if any error occurs, or the change is not pending.
        /// </summary>
        /// <param name="changeNo"> CL identifier. </param>
        /// <param name="changeListId"> Incude the text of branched and integrated files. </param>
        /// <returns> The change. </returns>
        public Change GetChange(string changeListId, bool includeBranchedFiles)
        {
            int changeNo;
            if (!Int32.TryParse(changeListId, out changeNo))
            {
                Console.WriteLine("Change List number is not a number!");
                return null;
            }

            string description = RunClient("describe " + changeNo, false);
            if (description == null)
                return null;

            StringReader reader = new StringReader(description);
            string firstLine = reader.ReadLine();
            if (firstLine == null)
            {
                Console.WriteLine("The description is empty. Cannot proceed.");
                return null;
            }

            Match firstLineMatch = ClientDescribeTitleParser.Match(firstLine);
            if (!firstLineMatch.Success)
            {
                Console.WriteLine("This change is not pending!");
                return null;
            }

            string clientName = firstLineMatch.Groups[1].Value;

            DateTime timeStamp;

            if (!DateTime.TryParse(firstLineMatch.Groups[2].Value, out timeStamp))
                BugOut(description + "\n\n[Could not parse the time stamp]");

            timeStamp = timeStamp.ToUniversalTime();

            if (!"".Equals(reader.ReadLine()))
                BugOut(description + "\n\n[No newline before change description]");

            StringBuilder changeDescription = new StringBuilder();
            for (; ; )
            {
                string line = reader.ReadLine();
                if (line == null)
                    BugOut(description + "\n\n[Unexpected EOL]");

                if (line.Equals("Affected files ..."))
                    break;

                changeDescription.Append(line.Trim());
                changeDescription.Append('\n');
            }

            if (!"".Equals(reader.ReadLine()))
                BugOut(description + "\n\n[No newline before the list of files]");

            List<ChangeFile> files = new List<ChangeFile>();
            for (; ; )
            {
                string fileString = reader.ReadLine();
                if (fileString == null || "".Equals(fileString))
                    break;

                Match match = ClientDescribeFileParser.Match(fileString);
                if (!match.Success)
                    BugOut(description + "\n\n[Could not match " + fileString + "]");

                string fileName = match.Groups[1].Value;
                string rev = match.Groups[2].Value;
                string action = match.Groups[3].Value;

                int revision = -1;
                if (!Int32.TryParse(rev, out revision))
                    BugOut(description + "\n\n[Could not parse revision in " + fileString + "]");

                // Make sure the file is text.
                string openedFile = RunClient("opened \"" + fileName + "\"", false);
                if (openedFile == null)
                    BugOut("opened \"" + fileName + "\"");

                // Ignore binary files
                bool isText = ClientOpenedIsText.IsMatch(openedFile);

                ChangeFile.SourceControlAction a = ChangeFile.SourceControlAction.ADD;
                if ("edit".Equals(action))
                    a = ChangeFile.SourceControlAction.EDIT;
                else if ("add".Equals(action))
                    a = ChangeFile.SourceControlAction.ADD;
                else if ("delete".Equals(action))
                    a = ChangeFile.SourceControlAction.DELETE;
                else if ("branch".Equals(action))
                    a = ChangeFile.SourceControlAction.BRANCH;
                else if ("integrate".Equals(action))
                    a = ChangeFile.SourceControlAction.INTEGRATE;
                else
                    BugOut(description + "\n\n[Unknown file action: " + action + "]");

                ChangeFile file = new ChangeFile(fileName, a, revision, isText);
                files.Add(file);
            }

            if (files.Count > 0)
            {
                Change change = new Change(SourceControl, clientName, changeListId, timeStamp,
                    changeDescription.ToString(), files.ToArray());
                FillInFileData(change, includeBranchedFiles);
                return change;
            }

            return null;
        }
Esempio n. 5
0
        /// <summary>
        /// Ensures that the diffs in files can in fact be parsed by Malevich. If non-graphical characters or
        /// incorrect (mixed: Unix + Windows, or Windows + Mac) line endings are present, this throws "sd diff"
        /// off and it produces the results that we will not be able to read. This checks that this does not occur.
        /// </summary>
        /// <param name="change"> Change List. </param>
        /// <returns> True if the differ is intact. </returns>
        private static bool VerifyDiffIntegrity(Change change)
        {
            Regex diffDecoder = new Regex(@"^([0-9]+)(,[0-9]+)?([a,d,c]).*$");
            bool result = true;
            foreach (SourceControl.ChangeFile file in change.Files)
            {
                if (file.Data == null ||
                    (file.Action != SourceControl.ChangeFile.SourceControlAction.EDIT &&
                    file.Action != SourceControl.ChangeFile.SourceControlAction.INTEGRATE))
                    continue;

                StringReader reader = new StringReader(file.Data);
                for (; ; )
                {
                    string line = reader.ReadLine();
                    if (line == null)
                        break;

                    if (line.StartsWith("> ") || line.StartsWith("< ") || line.Equals("---") ||
                        line.Equals("\\ No newline at end of file") || diffDecoder.IsMatch(line))
                        continue;

                    Console.WriteLine("Cannot parse the difference report for {0}.",
                        file.LocalOrServerFileName);
                    Console.WriteLine("{0}", file.Data);
                    Console.WriteLine();
                    result = false;
                    break;
                }
            }

            if (!result)
            {
                Console.WriteLine();
                Console.WriteLine("Found problems processing the file differences in the change list.");
                Console.WriteLine();
                Console.WriteLine("This is typically caused by incorrect or mixed end of line markers, or other");
                Console.WriteLine("non-graphical characters that your source control system could not process.");
                Console.WriteLine();
                Console.WriteLine("Please fix the files in question and resubmit the change.");
            }

            return result;
        }
        /// <summary>
        /// For every file in the change list, fill in its local path and either diff if it is an edit, or the
        /// file itself if it is an add.
        /// </summary>
        /// <param name="change"> The change list, instantiated. </param>
        /// <param name="includeBranchedFiles"> Include the text for branched and integrated files. </param>
        private void FillInFileData(Change change, bool includeBranchedFiles)
        {
            foreach (ChangeFile file in change.Files)
            {
                string where = RunClient("where \"" + file.ServerFileName + "\"", false);
                if (string.IsNullOrEmpty(where))
                    BugOut("sd|p4 where " + file.ServerFileName);

                Match files = ClientWhereParser.Match(where);
                if (!files.Success)
                    BugOut(where);

                file.LocalFileName = files.Groups[3].Value;
                if (File.Exists(file.LocalFileName))
                    file.LastModifiedTime = File.GetLastWriteTimeUtc(file.LocalFileName);

                if (!file.IsText)
                    continue;

                if (file.Action == ChangeFile.SourceControlAction.EDIT ||
                    (file.Action == ChangeFile.SourceControlAction.INTEGRATE && includeBranchedFiles))
                {
                    if (!file.IsShelved)
                        file.Data = RunClient("diff \"" + file.ServerFileName + "\"", true);
                    else
                    {
                        // Diff shelved file with diff2
                        // diff2 file#previousRevision file@=CL
                        file.Data = RunClient(string.Format("diff2 \"{0}#{1}\" \"{0}@={2}\"", file.ServerFileName, file.Revision, change.ChangeListId), true);
                    }
                }
                else if (file.Action == ChangeFile.SourceControlAction.ADD ||
                    (file.Action == ChangeFile.SourceControlAction.BRANCH && includeBranchedFiles))
                {
                    try
                    {
                        if (!file.IsShelved)
                        {
                            StreamReader reader = new StreamReader(file.LocalFileName);
                            file.Data = reader.ReadToEnd();
                            reader.Close();
                        }
                        else
                        {
                            file.Data = RunClient(string.Format("print -q \"{0}@={1}\"", file.ServerFileName, change.ChangeListId), false);
                        }
                    }
                    catch (FileNotFoundException)
                    {
                        Log.Error("File not found: " + file.LocalFileName);
                        throw new SourceControlRuntimeError("File not found: " + file.LocalFileName);
                    }
                }
            }
        }
        /// <summary>
        /// Gets the change from the source control system. The change must be pending.
        /// Returns null if any error occurs, or the change is not pending.
        /// </summary>
        /// <param name="changeNo"> CL identifier. </param>
        /// <param name="changeListId"> Incude the text of branched and integrated files. </param>
        /// <param name="skipFiles"> True to skip files when not needed - faster. </param>
        /// <returns> The change. </returns>
        public Change GetChange(string changeListId, bool includeBranchedFiles, bool skipFile = false)
        {
            bool shelved = false;
            int changeNo;
            if (!Int32.TryParse(changeListId, out changeNo))
            {
                Log.Error("Change List number is not a number!");
                return null;
            }

            // Use -S for shelved changelists
            string description = RunClient("describe -s -S " + changeNo, false);
            if (string.IsNullOrEmpty(description))
                return null;

            StringReader reader = new StringReader(description);
            string firstLine = reader.ReadLine();
            if (firstLine == null)
            {
                Log.Error("The description is empty. Cannot proceed.");
                return null;
            }

            Match firstLineMatch = ClientDescribeTitleParser.Match(firstLine);
            if (!firstLineMatch.Success)
            {
                Log.Error("This change is not pending!");
                return null;
            }

            string clientName = firstLineMatch.Groups[1].Value;

            DateTime timeStamp;

            if (!DateTime.TryParse(firstLineMatch.Groups[2].Value, out timeStamp))
                BugOut(description + "\n\n[Could not parse the time stamp]");

            timeStamp = timeStamp.ToUniversalTime();

            if (!"".Equals(reader.ReadLine()))
                BugOut(description + "\n\n[No newline before change description]");

            StringBuilder changeDescription = new StringBuilder();
            for (; ; )
            {
                string line = reader.ReadLine();
                if (line == null)
                    BugOut(description + "\n\n[Unexpected EOL]");

                if (line.Equals("Affected files ..."))
                    break;

                if (line.Equals("Shelved files ...")) {
                    shelved = true;
                    break;
                }

                changeDescription.Append(line.Trim());
                changeDescription.Append('\n');
            }

            if (!"".Equals(reader.ReadLine()))
                BugOut(description + "\n\n[No newline before the list of files]");

            List<ChangeFile> files = new List<ChangeFile>();
            if (!skipFile)
            {
                for (; ; )
                {
                    string fileString = reader.ReadLine();
                    if (fileString == null || "".Equals(fileString))
                        break;

                    Match match = ClientDescribeFileParser.Match(fileString);
                    if (!match.Success)
                        BugOut(description + "\n\n[Could not match " + fileString + "]");

                    string fileName = match.Groups[1].Value;
                    string rev = match.Groups[2].Value;
                    string action = match.Groups[3].Value;

                    int revision = -1;
                    if (rev != "none" && !Int32.TryParse(rev, out revision))
                        BugOut(description + "\n\n[Could not parse revision in " + fileString + "]");

                    // Make sure the file is text.
                    bool isText = false;
                    if (!shelved)
                    {
                        var openedFile = RunClient("opened \"" + fileName + "\"", false);
                        if (string.IsNullOrEmpty(openedFile) && !shelved)
                            BugOut("opened \"" + fileName + "\"");

                        // Ignore binary files
                        isText = ClientOpenedIsText.IsMatch(openedFile);
                    }
                    else
                    {
                        var stats = RunClient(string.Format("fstat \"{0}@={1}\"", fileName, changeListId), false);
                        if (string.IsNullOrEmpty(stats) && !shelved)
                            BugOut("opened \"" + fileName + "\"");

                        // Ignore binary files
                        isText = stats.Contains("... headType text") ? true : false;
                    }

                    ChangeFile.SourceControlAction a = ChangeFile.SourceControlAction.ADD;
                    if ("edit".Equals(action))
                        a = ChangeFile.SourceControlAction.EDIT;
                    else if ("add".Equals(action) || "move/add".Equals(action))
                        a = ChangeFile.SourceControlAction.ADD;
                    else if ("delete".Equals(action) || "move/delete".Equals(action))
                        a = ChangeFile.SourceControlAction.DELETE;
                    else if ("branch".Equals(action))
                        a = ChangeFile.SourceControlAction.BRANCH;
                    else if ("integrate".Equals(action))
                        a = ChangeFile.SourceControlAction.INTEGRATE;
                    else if ("move/add".Equals(action))
                        a = ChangeFile.SourceControlAction.RENAME;
                    else
                        BugOut(description + "\n\n[Unknown file action: " + action + "]");

                    ChangeFile file = new ChangeFile(fileName, a, revision, isText, shelved);
                    files.Add(file);
                }
            }

            if (files.Count > 0 || skipFile)
            {
                Change change = new Change(SourceControl, clientName, changeListId, timeStamp,
                    description, files.ToArray());
                FillInFileData(change, includeBranchedFiles);
                return change;
            }

            return null;
        }