Пример #1
0
        /// <summary>
        /// Log file has format specified in GitCommandLine class
        /// </summary>
        private ChangeSetHistory ParseLog(Stream log, Graph graph)
        {
            var changeSets = new List <ChangeSet>();

            using (var reader = new StreamReader(log))
            {
                var proceed = GoToNextRecord(reader);
                if (!proceed)
                {
                    return(new ChangeSetHistory(new List <ChangeSet>()));

                    // I found one case where git log --follow caused an empty output.
                    //throw new FormatException("The file does not contain any change sets.");
                }

                while (proceed)
                {
                    var changeSet = ParseRecord(reader, graph);
                    changeSets.Add(changeSet);
                    proceed = GoToNextRecord(reader);
                }
            }

            var history = new ChangeSetHistory(changeSets.OrderByDescending(x => x.Date).ToList());

            return(history);
        }
Пример #2
0
        public void WriteDebugGraph(ChangeSetHistory history, Graph graph, string targetHash, string findMe)
        {
            var dbgGraph = graph.Clone();

            dbgGraph.MinimizeTo(targetHash);


            var highlightedNodes = new Dictionary <GraphNode, string>();

            foreach (var node in dbgGraph.AllNodes)
            {
                var changeSet   = history.ChangeSets.Single(cs => cs.Id == node.CommitHash);
                var actionsDone = changeSet.Items.Where(item => item.ServerPath.Contains(findMe))
                                  .Select(FormatItem).ToList();
                if (actionsDone.Any())
                {
                    highlightedNodes.Add(node, string.Join("\n", actionsDone));
                }
            }

            var shortHash = targetHash.Substring(0, 5);
            var file      = Path.Combine(_directory, $"conflict_graph_{shortHash}_{findMe}.dot");

            var graphWriter = new GraphWriter();

            graphWriter.SaveGraphSimplified(file, dbgGraph, highlightedNodes);
        }
Пример #3
0
        /// <summary>
        ///     If the classifier returns string.EMPTY the according file is not used.
        /// </summary>
        public List <Coupling> CalculateClassifiedChangeCouplings(ChangeSetHistory history, Func <string, string> classifier)
        {
            _couplings.Clear();
            _count.Clear();

            foreach (var cs in history.ChangeSets)
            {
                var classifications = ClassifyItems(cs, classifier);
                IncrementCommitCount(classifications);

                for (var i = 0; i < classifications.Count - 1; i++) // Keep one for the last pair
                {
                    // Make pairs of code classifiers.
                    for (var j = i + 1; j < classifications.Count; j++)
                    {
                        var class1 = classifications[i];
                        var class2 = classifications[j];

                        IncrementCoupling(class1, class2);
                    }
                }
            }

            CalculateDegree();

            return(_couplings.Values
                   //  .Where(coupling => coupling.Couplings >= Thresholds.MinCouplingForChangeCoupling && coupling.Degree >= Thresholds.MinDegreeForChangeCoupling)
                   .OrderByDescending(coupling => coupling.Degree).ToList());
        }
        /// <summary>
        /// Converts a given <see cref="T:XElement"/> into a <see cref="T:ChangeSetHistory"/>.
        /// </summary>
        /// <param name="element">The <see cref="T:XElement"/> to convert.</param>
        /// <returns>The newly created <see cref="T:ChangeSetHistory"/>.</returns>
        public ChangeSetHistory FromXElement(XElement element)
        {
            ChangeSetHistory history = new ChangeSetHistory();
            foreach (XElement changeSetElement in element.Nodes()) {
                ChangeSet changeSet = new ChangeSet();

                // Get the change set details
                foreach (XElement changeSetElementChild in changeSetElement.Nodes()) {
                    if (changeSetElementChild.Name.LocalName == "Applied") {
                        changeSet.Applied = DateTime.Parse(changeSetElementChild.Value);
                    } else if (changeSetElementChild.Name.LocalName == "Username") {
                        changeSet.Username = changeSetElementChild.Value;
                    } else if (changeSetElementChild.Name.LocalName == "Change") {
                        Change change = new Change();
                        foreach (XElement changeElement in changeSetElementChild.Nodes()) {
                            if (changeElement.Name.LocalName == "PropertyName") {
                                change.PropertyName = changeElement.Value;
                            } else if (changeElement.Name.LocalName == "OldValue") {
                                change.OldValue = changeElement.Value;
                            } else if (changeElement.Name.LocalName == "NewValue") {
                                change.NewValue = changeElement.Value;
                            }
                        }
                        changeSet.Changes.Add(change);
                    }
                }
                history.Append(changeSet);
            }
            return history;
        }
Пример #5
0
        private ChangeSetHistory ReadExportFile()
        {
            _tracking = new MovementTracker();
            var result = new List <ChangeSet>();

            using (var reader = XmlReader.Create(_logFile))
            {
                while (reader.Read())
                {
                    if (reader.IsStartElement("logentry"))
                    {
                        ParseLogEntry(reader, result);
                    }
                }
            }

            // First item is latest commit because we got the by ordering
            Debug.Assert(result.First().Date >= result.Last().Date);

            Warnings = _tracking.Warnings;
            var history = new ChangeSetHistory(result);

            history.CleanupHistory();
            return(history);
        }
Пример #6
0
 private void LoadHistory()
 {
     if (_history == null)
     {
         // Assume the history was already cleaned such that it only contains tracked files and no deletes.
         _history = _sourceProvider.QueryChangeSetHistory();
     }
 }
Пример #7
0
        void LoadHistory()
        {
            if (_history == null)
            {
                var provider = Project.CreateProvider();
                _history = provider.QueryChangeSetHistory();
                Warnings = provider.Warnings;

                // Remove all items that are deleted now.
                _history.CleanupHistory();
            }
        }
Пример #8
0
        public List <Coupling> CalculateChangeCouplings(ChangeSetHistory history, IFilter filter)
        {
            _couplings.Clear();
            _count.Clear();

            var idToLocalFile = BuildIdToLocalFileMap(history);

            foreach (var cs in history.ChangeSets)
            {
                if (cs.Items.Count > Thresholds.MaxItemsInChangesetForChangeCoupling)
                {
                    continue;
                }

                // Only accepted files
                var itemIds = cs.Items.Where(item => filter.IsAccepted(item.LocalPath)).Select(item => item.Id).ToList();

                // Do you have uncommitted changes.
                // Do you have commit items not inside the base directory?
                var missingFiles = itemIds.Select(id => idToLocalFile[id]).Where(file => !File.Exists(file));
                // Debug.Assert(!missingFiles.Any());

                IncrementCommitCount(itemIds);

                for (var i = 0; i < itemIds.Count - 1; i++) // Keep one for the last pair
                {
                    // Make pairs of files.
                    for (var j = i + 1; j < itemIds.Count; j++)
                    {
                        var id1 = itemIds[i];
                        var id2 = itemIds[j];

                        IncrementCoupling(id1, id2);
                    }
                }
            }

            CalculateDegree();

            return(_couplings.Values
                   .Where(coupling => coupling.Couplings >= Thresholds.MinCouplingForChangeCoupling && coupling.Degree >= Thresholds.MinDegreeForChangeCoupling)
                   .OrderByDescending(coupling => coupling.Degree)
                   .Select(c => new Coupling(idToLocalFile[c.Item1], idToLocalFile[c.Item2])
            {
                // Display coupling item with local path instead of identifier.
                Degree = c.Degree,
                Couplings = c.Couplings
            }).ToList());
        }
Пример #9
0
        /// <summary>
        /// returns the team communication path.
        /// If no team classifier is provided, the developer himself is used.
        /// </summary>
        public Dictionary <OrderedPair, uint> AnalyzeTeamCommunication(ChangeSetHistory history, ITeamClassifier teamClassifier)
        {
            // file id -> dictionary{team, #commits}
            var fileToCommitsPerTeam = new Dictionary <string, Dictionary <string, uint> >();

            foreach (var cs in history.ChangeSets)
            {
                // Associated team (or developer)
                var team = GetTeam(cs, teamClassifier);

                foreach (var item in cs.Items)
                {
                    // Add team to file
                    if (!fileToCommitsPerTeam.Keys.Contains(item.Id))
                    {
                        fileToCommitsPerTeam.Add(item.Id, new Dictionary <string, uint>());
                    }

                    var commitsPerTeam = fileToCommitsPerTeam[item.Id];
                    commitsPerTeam.AddToValue(team, 1);
                }
            }

            // We know for each file which team did how many changes
            // Each file that was accessed by two teams leads to a link between the teams.

            // pair of teams -> number of commits.
            var teamCommunicationPaths = new Dictionary <OrderedPair, uint>();

            foreach (var file in fileToCommitsPerTeam)
            {
                var currentFileTeams = file.Value.Keys.ToList();

                // Build team subsets that get counted as one path.
                for (var index = 0; index < currentFileTeams.Count - 1; index++)
                {
                    for (var index2 = 1; index2 < currentFileTeams.Count; index2++)
                    {
                        var teamPair = new OrderedPair(currentFileTeams[index], currentFileTeams[index2]);

                        // The team combination that worked together at least one time at the same file
                        // gets a point.
                        teamCommunicationPaths.AddToValue(teamPair, 1);
                    }
                }
            }

            return(teamCommunicationPaths);
        }
Пример #10
0
        /// <summary>
        /// Seed is the file name the file was first committed under.
        /// </summary>
        private void AssertFile(ChangeSetHistory history, string fileSeed, int changeSets, int add, int modify, int delete, int rename, int copy, string finalName)
        {
            var id = FindId(history, fileSeed);

            Assert.IsNotNull(id);
            var stats = Follow(history, id);

            Assert.AreEqual(changeSets, stats.ChangesSets);
            Assert.AreEqual(add, stats.Add, "Add");
            Assert.AreEqual(modify, stats.Modify, "Modify");
            Assert.AreEqual(rename, stats.Rename, "Rename");
            Assert.AreEqual(copy, stats.Copy, "Copy");
            Assert.AreEqual(delete, stats.Delete, "Delete");
            Assert.AreEqual(finalName, stats.FinalName, "FinalName");
        }
Пример #11
0
        public List <Coupling> CalculateChangeCouplings(ChangeSetHistory history, IFilter filter)
        {
            _couplings.Clear();
            _count.Clear();

            var idToLocalFile = BuildIdToLocalFileMap(history);

            foreach (var cs in history.ChangeSets)
            {
                if (cs.Items.Count > Thresholds.MaxItemsInChangesetForChangeCoupling)
                {
                    continue;
                }

                // Only accepted files
                // Normally the files are already filtered when the history was created.
                var itemIds = cs.Items.Where(item => filter.IsAccepted(item.LocalPath)).Select(item => item.Id).ToList();

                IncrementCommitCount(itemIds);

                for (var i = 0; i < itemIds.Count - 1; i++) // Keep one for the last pair
                {
                    // Make pairs of files.
                    for (var j = i + 1; j < itemIds.Count; j++)
                    {
                        var id1 = itemIds[i];
                        var id2 = itemIds[j];

                        IncrementCoupling(id1, id2);
                    }
                }
            }

            CalculateDegree();

            return(_couplings.Values
                   .Where(coupling => coupling.Couplings >= Thresholds.MinCouplingForChangeCoupling && coupling.Degree >= Thresholds.MinDegreeForChangeCoupling)
                   .OrderByDescending(coupling => coupling.Degree)
                   .Select(c => new Coupling(idToLocalFile[c.Item1], idToLocalFile[c.Item2])
            {
                // Display coupling item with local path instead of identifier.
                Degree = c.Degree,
                Couplings = c.Couplings
            }).ToList());
        }
Пример #12
0
        private static Dictionary <string, string> BuildIdToLocalFileMap(ChangeSetHistory history)
        {
            var idToLocalFile = new Dictionary <string, string>();

            foreach (var cs in history.ChangeSets)
            {
                foreach (var item in cs.Items)
                {
                    if (!idToLocalFile.ContainsKey(item.Id))
                    {
                        // Seen the first time means latest file.
                        idToLocalFile.Add(item.Id, item.LocalPath);
                    }
                }
            }

            return(idToLocalFile);
        }
 /// <summary>
 /// Converts a given <see cref="ChangeSetHistory"/> into an <see cref="T:XElement"/>.
 /// </summary>
 /// <param name="changeSetHistory">The <see cref="ChangeSetHistory"/> to convert.</param>
 /// <returns>The newly created <see cref="T:XElement"/>.</returns>
 public XElement ToXElement(ChangeSetHistory changeSetHistory)
 {
     XElement historyElement = new XElement("ChangeSetHistory");
     foreach (ChangeSet changeSet in changeSetHistory) {
         XElement changeSetElement = new XElement("ChangeSet",
             new XElement("Applied", changeSet.Applied),
             new XElement("Username", changeSet.Username));
         foreach (Change change in changeSet.Changes) {
             XElement changeElement = new XElement("Change",
                 new XElement("PropertyName", change.PropertyName),
                 new XElement("OldValue", change.OldValue),
                 new XElement("NewValue", change.NewValue));
             changeSetElement.Add(changeElement);
         }
         historyElement.Add(changeSetElement);
     }
     return historyElement;
 }
Пример #14
0
        private void UpdateHistory(IProgress progress)
        {
            // Git graph
            _graph         = new Graph();
            _idToLocalFile = new Dictionary <string, string>();


            // Build the full graph
            var fullLog = _gitCli.Log();
            var parser  = new Parser(_mapper);

            var(_, graph) = parser.ParseLogString(fullLog);
            _graph        = graph;

            // Save the original log for information (debugging)
            //SaveFullLogToDisk();

            // Build a virtual commit history
            var localPaths = GetAllTrackedLocalFiles();

            var changeSets = RebuildHistory(localPaths, progress);

            // file id -> List of change set id
            var filesToRemove = FindSharedHistory(changeSets);

            // Cleanup history. We stop tracking a file if it starts sharing its history with another file.
            // The history may skip some commits so we use the full graph to traverse everything.
            DeleteSharedHistory(changeSets, filesToRemove);

            // Write history file
            var history = new ChangeSetHistory(changeSets);

            history.CleanupHistory(); // Assume nothing is removed since no deletes should be in history.
            var json = JsonConvert.SerializeObject(history, Formatting.Indented);

            File.WriteAllText(_historyFile, json, Encoding.UTF8);

            // Save the constructed log for information
            SaveRecoveredLogToDisk(changeSets, _graph);

            // Just a plausibility check
            VerifyNoDuplicateServerPathsInChangeset(changeSets);
        }
Пример #15
0
        private static string FindId(ChangeSetHistory history, string fileSeed)
        {
            string id = null;

            foreach (var cs in history.ChangeSets)
            {
                // old to new
                foreach (var item in cs.Items)
                {
                    if (item.ServerPath == fileSeed && item.IsAdd())
                    {
                        id = item.Id;
                        break;
                    }
                }
            }

            return(id);
        }
Пример #16
0
        private GitProviderTests.FollowResult Follow(ChangeSetHistory history, string id)
        {
            var result = new GitProviderTests.FollowResult();

            foreach (var cs in history.ChangeSets)
            {
                var ofId = cs.Items.Where(item => item.Id == id).ToList();
                if (ofId.Any())
                {
                    result.ChangesSets++;
                }

                foreach (var item in ofId)
                {
                    IncrementOperations(result, item);
                }
            }

            return(result);
        }
Пример #17
0
        public bool Verify(Graph graph, GraphNode node, ChangeSetHistory history)
        {
            var expectedServerPaths = GetAllTrackedFiles(node.CommitHash);
            var actualServerPaths   = node.Scope.GetAllFiles();

            var intersect = new HashSet <string>(expectedServerPaths.Intersect(actualServerPaths));
            var inGit     = new HashSet <string>(expectedServerPaths.Except(intersect));
            var inScope   = new HashSet <string>(actualServerPaths.Except(intersect));

            //var differences = expectedServerPaths;
            //differences.SymmetricExceptWith(actualServerPaths);

            var union = inScope.Union(inGit).ToList();

            if (union.Any())
            {
                // Save differences
                _debugLogFile.WriteLine("Deviation from scope and expected git tree");
                WriteDifference(inGit, "Git");
                WriteDifference(inScope, "Scope");

                // Save graphs
                foreach (var serverPath in union)
                {
                    var fi = new FileInfo(serverPath);
                    WriteDebugGraph(history, graph, node.CommitHash, fi.Name);
                }

                // Write differences to a separate file
                //File.WriteAllText(Path.Combine(_directory, $"conflict_diff_{shortHash}.txt"), builder.ToString());

                return(false);
            }

            return(true);
        }
Пример #18
0
        /// <summary>
        /// Log file has format specified in GitCommandLine class
        /// </summary>
        ChangeSetHistory ParseLog(Stream log)
        {
            var changeSets = new List <ChangeSet>();

            using (var reader = new StreamReader(log))
            {
                var proceed = GoToNextRecord(reader);
                if (!proceed)
                {
                    throw new FormatException("The file does not contain any change sets.");
                }

                while (proceed)
                {
                    var changeSet = ParseRecord(reader);
                    changeSets.Add(changeSet);
                    proceed = GoToNextRecord(reader);
                }
            }

            var history = new ChangeSetHistory(changeSets.OrderByDescending(x => x.Date).ToList());

            return(history);
        }
Пример #19
0
        /// <summary>
        ///     Creates the history in a form that we can process further from initial commit to the last one.
        /// </summary>
        private ChangeSetHistory CreateHistory(Repository repo, Graph graph, IProgress progress)
        {
            var nodeCount   = graph.AllNodes.Count();
            var currentNode = 0;

            var changeSets = new List <ChangeSet>();

            var initialNodes = graph.AllNodes.Where(node => node.Parents.Any() is false).ToList();

            Debug.Assert(initialNodes.Count == 1);
            var initialNode = initialNodes.First();

            var alreadyProcessed = new HashSet <string>();
            var nodesToProcess   = new Queue <GraphNode>();

            nodesToProcess.Enqueue(initialNode);
            while (nodesToProcess.Any())
            {
                var node = nodesToProcess.Dequeue();

                if (IsMergeWithUnprocessedParents(node))
                {
                    // We arrive here later again.
                    continue;
                }

                if (!alreadyProcessed.Add(node.CommitHash))
                {
                    // When we arrive a merge node the first time both parents may be complete (or not).
                    // If so we would end up following the same path twice.
                    continue;
                }

                progress?.Message($"Processing commit {++currentNode} / {nodeCount}");

                var commit      = repo.Lookup <Commit>(node.CommitHash);
                var differences = CalculateDiffs(repo, commit);

                var(scope, deletedServerPathToId) = ApplyChangesToScope(node, differences);
                node.Scope = scope;


                var cs = CreateChangeSet(commit);
                changeSets.Add(cs);

                // Create change items
                foreach (var change in differences.ChangesInCommit)
                {
                    var item = CreateChangeItem(change, scope, deletedServerPathToId);
                    cs.Items.Add(item);
                }

                // Add children to processing queue
                foreach (var childNode in node.Children)
                {
                    nodesToProcess.Enqueue(childNode);
                }
            }

            VerifyScope(graph.GetNode(repo.Head.Tip.Sha));

            var history = new ChangeSetHistory(changeSets.OrderByDescending(cs => cs.Date).ToList());

            return(history);
        }
Пример #20
0
        protected void SaveHistory(ChangeSetHistory history)
        {
            var json = JsonConvert.SerializeObject(history, Formatting.Indented);

            File.WriteAllText(_historyFile, json, Encoding.UTF8);
        }
Пример #21
0
 internal void Clear()
 {
     _history       = null;
     _metrics       = null;
     _contributions = null;
 }