Ejemplo n.º 1
0
        /// <summary>
        /// Add a single revision from the git log.
        /// </summary>
        public void Add(GitRevision revision, RevisionNodeFlags types)
        {
            if (!_nodeByObjectId.TryGetValue(revision.ObjectId, out RevisionGraphRevision revisionGraphRevision))
            {
                // This revision is added from the log, but not seen before. This is probably a root node (new branch) OR the revisions
                // are not in topo order. If this the case, we deal with it later.
                revisionGraphRevision           = new RevisionGraphRevision(revision.ObjectId, ++_maxScore);
                revisionGraphRevision.LaneColor = revisionGraphRevision.IsCheckedOut ? 0 : _maxScore;

                _nodeByObjectId.TryAdd(revision.ObjectId, revisionGraphRevision);
            }
            else
            {
                // This revision was added earlier, but is now found in the log.
                // Increase the score to the current maxScore to keep the order in tact.
                revisionGraphRevision.EnsureScoreIsAbove(++_maxScore);
            }

            // This revision may have been added as a parent before. Probably only the ObjectId is known. Set all the other properties.
            revisionGraphRevision.GitRevision = revision;
            revisionGraphRevision.ApplyFlags(types);

            // No build the revisions parent/child structure. The parents need to added here. The child structure is kept in synch in
            // the RevisionGraphRevision class.
            if (revision.ParentIds is not null)
            {
                foreach (ObjectId parentObjectId in revision.ParentIds)
                {
                    if (!_nodeByObjectId.TryGetValue(parentObjectId, out RevisionGraphRevision parentRevisionGraphRevision))
                    {
                        // This parent is not loaded before. Create a new (partial) revision. We will complete the info in the revision
                        // when this revision is loaded from the log.
                        parentRevisionGraphRevision = new RevisionGraphRevision(parentObjectId, ++_maxScore);
                        _nodeByObjectId.TryAdd(parentObjectId, parentRevisionGraphRevision);
                    }
                    else
                    {
                        // This revision is already loaded, add the existing revision to the parents list of new revision.
                        // If the current score is lower, cache is invalid. The new score will (probably) be higher.
                        MarkCacheAsInvalidIfNeeded(parentRevisionGraphRevision);
                    }

                    // Store the newly created segment (connection between 2 revisions)
                    revisionGraphRevision.AddParent(parentRevisionGraphRevision, out int newMaxScore);
                    _maxScore = Math.Max(_maxScore, newMaxScore);

                    if (types.HasFlag(RevisionNodeFlags.OnlyFirstParent))
                    {
                        break;
                    }
                }
            }

            // Ensure all parents are loaded before adding it to the _nodes list. This is important for ordering.
            ImmutableInterlocked.Update(ref _nodes, (list, revision) => list.Add(revision), revisionGraphRevision);
        }
Ejemplo n.º 2
0
        // Add a single revision from the git log.
        public void Add(GitRevision revision, RevisionNodeFlags types)
        {
            if (!_nodeByObjectId.TryGetValue(revision.ObjectId, out RevisionGraphRevision revisionGraphRevision))
            {
                // This revision is added from the log, but not seen before. This is probably a root node (new branch) OR the revisions
                // are not in topo order. If this the case, we deal with it later.
                revisionGraphRevision = new RevisionGraphRevision(revision.ObjectId, ++_maxScore);
                revisionGraphRevision.ApplyFlags(types);
                revisionGraphRevision.LaneColor = revisionGraphRevision.IsCheckedOut ? 0 : _maxScore;

                revisionGraphRevision.GitRevision = revision;
                _nodeByObjectId.TryAdd(revision.ObjectId, revisionGraphRevision);
                _nodes.Add(revisionGraphRevision);
            }
            else
            {
                // This revision was added as a parent before. Probably only the objectid is known. Set all the other properties.
                revisionGraphRevision.GitRevision = revision;
                revisionGraphRevision.ApplyFlags(types);

                // Since this revision was added earlier, but is now found in the log. Increase the score to the current maxScore
                // to keep the order ok.
                revisionGraphRevision.EnsureScoreIsAbove(++_maxScore);
                _nodes.Add(revisionGraphRevision);
            }

            // No build the revisions parent/child structure. The parents need to added here. The child structure is kept in synch in
            // the RevisionGraphRevision class.
            foreach (ObjectId parentObjectId in revision.ParentIds)
            {
                if (!_nodeByObjectId.TryGetValue(parentObjectId, out RevisionGraphRevision parentRevisionGraphRevision))
                {
                    // This parent is not loaded before. Create a new (partial) revision. We will complete the info in the revision
                    // when this revision is loaded from the log.
                    parentRevisionGraphRevision = new RevisionGraphRevision(parentObjectId, ++_maxScore);
                    _nodeByObjectId.TryAdd(parentObjectId, parentRevisionGraphRevision);

                    // Store the newly created segment (connection between 2 revisions)
                    revisionGraphRevision.AddParent(parentRevisionGraphRevision, out int newMaxScore);
                    _maxScore = Math.Max(_maxScore, newMaxScore);
                }
                else
                {
                    // This revision is already loaded, add the existing revision to the parents list of new revision.
                    // If the current score is lower, cache is invalid. The new score will (probably) be higher.
                    ResetCacheIfNeeded(parentRevisionGraphRevision);

                    // Store the newly created segment (connection between 2 revisions)
                    revisionGraphRevision.AddParent(parentRevisionGraphRevision, out int newMaxScore);
                    _maxScore = Math.Max(_maxScore, newMaxScore);
                }
            }

            ResetCacheIfNeeded(revisionGraphRevision);
        }
Ejemplo n.º 3
0
 public void ApplyFlags(RevisionNodeFlags types)
 {
     IsRelative  |= (types & RevisionNodeFlags.CheckedOut) != 0;
     HasRef       = (types & RevisionNodeFlags.HasRef) != 0;
     IsCheckedOut = (types & RevisionNodeFlags.CheckedOut) != 0;
 }
Ejemplo n.º 4
0
        public void Add(GitRevision revision, RevisionNodeFlags flags)
        {
            var parentIds = revision.ParentIds;

            // If we haven't seen this node yet, create a new junction.
            // We process revisions in reverse order.
            // For each revision we create a node for the revision and its parents.
            // Most of the time we will have a node for this revision, created for
            // a parent of revision processed beforehand.
            if (!GetOrCreateNode(revision.ObjectId, out var node) && (parentIds == null || parentIds.Count == 0))
            {
                // The revision has not been seen yet -- it must be a leaf node.
                // Create a junction for it.
                _junctions.Add(new Junction(node));
            }

            Count++;
            node.Revision = revision;
            node.Flags    = flags;
            node.Index    = _nodes.Count;
            _nodes.Add(node);

            var isCheckedOut = flags.HasFlag(RevisionNodeFlags.CheckedOut);

            foreach (var parentId in parentIds ?? Array.Empty <ObjectId>())
            {
                GetOrCreateNode(parentId, out var parent);

                if (parent.Index < node.Index)
                {
                    // TODO: We might be able to recover from this with some work, but
                    // since we build the graph async it might be tough to figure out.
                    Debug.WriteLine("The nodes must be added such that all children are added before their parents");
                    continue;
                }

                if (node.Descendants.Count == 1 &&
                    node.Ancestors.Count <= 1 &&
                    node.Descendants[0].Oldest == node &&
                    parent.Ancestors.Count == 0 &&

                    // If this is true, the current revision is in the middle of a branch
                    // and is about to start a new branch. This will also mean that the last
                    // revisions are non-relative. Make sure a new junction is added and this
                    // is the start of a new branch (and color!)
                    !isCheckedOut)
                {
                    // The node isn't a junction point. Just the parent to the node's
                    // (only) ancestor junction.
                    node.Descendants[0].Add(parent);
                }
                else if (node.Ancestors.Count == 1 && node.Ancestors[0].Youngest != node)
                {
                    // The node is in the middle of a junction. We need to split it.
                    _junctions.Add(node.Ancestors[0].SplitIntoJunctionWith(node));

                    // The node is a junction point. We are a new junction
                    _junctions.Add(new Junction(node, parent));
                }
                else if (parent.Descendants.Count == 1 && parent.Descendants[0].Oldest != parent)
                {
                    // The parent is in the middle of a junction. We need to split it.
                    _junctions.Add(parent.Descendants[0].SplitIntoJunctionWith(parent));

                    // The node is a junction point. We are a new junction
                    _junctions.Add(new Junction(node, parent));
                }
                else
                {
                    // The node is a junction point. We are a new junction
                    _junctions.Add(new Junction(node, parent));
                }
            }

            var isRelative = isCheckedOut || node.Descendants.Any(d => d.IsRelative);

            var needsRebuild = false;

            foreach (var ancestor in node.Ancestors)
            {
                ancestor.IsRelative |= isRelative;

                // Uh, oh, we've already processed this lane. We'll have to update some rows.
                var parent = ancestor.TryGetParent(node);

                if (parent != null && parent.InLane != int.MaxValue)
                {
                    Debug.WriteLine("We have to start over at lane {0} because of {1}",
                                    ancestor.Oldest.Descendants.Aggregate(ancestor.Oldest.InLane, (current, dd) => Math.Min(current, dd.Youngest.InLane)),
                                    node);

                    needsRebuild = true;
                    break;
                }
            }

            if (needsRebuild)
            {
                // TODO: It would be nice if we didn't have to start completely over...but it wouldn't
                // be easy since we don't keep around all of the necessary lane state for each step.
                var lastLaneIndex = Count - 1;

                ClearLanes();
                CacheTo(lastLaneIndex);

                // We need to redraw everything
                Updated?.Invoke();
            }
            else
            {
                Update(node);
            }

            bool GetOrCreateNode(ObjectId objectId, out Node n)
            {
                if (!_nodeByObjectId.TryGetValue(objectId, out n))
                {
                    n = new Node(objectId);
                    _nodeByObjectId.Add(objectId, n);
                    return(false);
                }

                return(true);
            }
        }