Beispiel #1
0
        /// <summary>
        /// Updates entries in the current index with file sizes and times
        /// Algorithm:
        ///     1) If there was an index in place when this object was constructed, then:
        ///      a) Copy all valid entries (below) from the previous index to the new index
        ///      b) Conditionally (below) get times/sizes from the working tree for files not updated from the previous index
        ///
        ///     2) If there was no index in place, conditionally populate all entries from disk
        ///
        /// Conditions:
        /// - Working tree is only searched if allowUpdateFromWorkingTree is specified
        /// - A valid entry is an entry that exist and has a non-zero creation time (ctime)
        /// </summary>
        /// <param name="addedOrEditedLocalFiles">A collection of added or edited files</param>
        /// <param name="allowUpdateFromWorkingTree">Set to true if the working tree is known good and can be used during the update.</param>
        /// <param name="backupIndex">An optional index to source entry values from</param>
        public void UpdateFileSizesAndTimes(BlockingCollection <string> addedOrEditedLocalFiles, bool allowUpdateFromWorkingTree, bool shouldSignIndex, Index backupIndex = null)
        {
            if (this.readOnly)
            {
                throw new InvalidOperationException("Cannot update a readonly index.");
            }

            using (ITracer activity = this.tracer.StartActivity("UpdateFileSizesAndTimes", EventLevel.Informational, Keywords.Telemetry, null))
            {
                File.Copy(this.indexPath, this.updatedIndexPath, overwrite: true);

                this.Parse();

                bool anyEntriesUpdated = false;

                using (MemoryMappedFile mmf = this.GetMemoryMappedFile())
                    using (MemoryMappedViewAccessor indexView = mmf.CreateViewAccessor())
                    {
                        // Only populate from the previous index if we believe it's good to populate from
                        // For now, a current FastFetch version marker is the only criteria
                        if (backupIndex != null)
                        {
                            if (this.IsFastFetchVersionMarkerCurrent())
                            {
                                using (this.tracer.StartActivity("UpdateFileInformationFromPreviousIndex", EventLevel.Informational, Keywords.Telemetry, null))
                                {
                                    anyEntriesUpdated |= this.UpdateFileInformationForAllEntries(indexView, backupIndex, allowUpdateFromWorkingTree);
                                }

                                if (addedOrEditedLocalFiles != null)
                                {
                                    // always update these files from disk or the index won't have good information
                                    // for them and they'll show as modified even those not actually modified.
                                    anyEntriesUpdated |= this.UpdateFileInformationFromDiskForFiles(indexView, addedOrEditedLocalFiles);
                                }
                            }
                        }
                        else if (allowUpdateFromWorkingTree)
                        {
                            // If we didn't update from a previous index, update from the working tree if allowed.
                            anyEntriesUpdated |= this.UpdateFileInformationFromWorkingTree(indexView);
                        }

                        indexView.Flush();
                    }

                if (anyEntriesUpdated)
                {
                    this.MoveUpdatedIndexToFinalLocation(shouldSignIndex);
                }
                else
                {
                    File.Delete(this.updatedIndexPath);
                }
            }
        }
Beispiel #2
0
        /// <param name="branchOrCommit">A specific branch to filter for, or null for all branches returned from info/refs</param>
        public override void FastFetch(string branchOrCommit, bool isBranch)
        {
            if (string.IsNullOrWhiteSpace(branchOrCommit))
            {
                throw new FetchException("Must specify branch or commit to fetch");
            }

            GitRefs refs = null;
            string  commitToFetch;

            if (isBranch)
            {
                refs = this.ObjectRequestor.QueryInfoRefs(branchOrCommit);
                if (refs == null)
                {
                    throw new FetchException("Could not query info/refs from: {0}", this.Enlistment.RepoUrl);
                }
                else if (refs.Count == 0)
                {
                    throw new FetchException("Could not find branch {0} in info/refs from: {1}", branchOrCommit, this.Enlistment.RepoUrl);
                }

                commitToFetch = refs.GetTipCommitId(branchOrCommit);
            }
            else
            {
                commitToFetch = branchOrCommit;
            }

            this.DownloadMissingCommit(commitToFetch, this.GitObjects);

            // Configure pipeline
            // Checkout uses DiffHelper when running checkout.Start(), which we use instead of LsTreeHelper like in FetchHelper.cs
            // Checkout diff output => FindMissingBlobs => BatchDownload => IndexPack => Checkout available blobs
            CheckoutJob            checkout    = new CheckoutJob(this.checkoutThreadCount, this.FolderList, commitToFetch, this.Tracer, this.Enlistment);
            FindMissingBlobsJob    blobFinder  = new FindMissingBlobsJob(this.SearchThreadCount, checkout.RequiredBlobs, checkout.AvailableBlobShas, this.Tracer, this.Enlistment);
            BatchObjectDownloadJob downloader  = new BatchObjectDownloadJob(this.DownloadThreadCount, this.ChunkSize, blobFinder.DownloadQueue, checkout.AvailableBlobShas, this.Tracer, this.Enlistment, this.ObjectRequestor, this.GitObjects);
            IndexPackJob           packIndexer = new IndexPackJob(this.IndexThreadCount, downloader.AvailablePacks, checkout.AvailableBlobShas, this.Tracer, this.GitObjects);

            // Start pipeline
            downloader.Start();
            blobFinder.Start();
            checkout.Start();

            blobFinder.WaitForCompletion();
            this.HasFailures |= blobFinder.HasFailures;

            // Delay indexing. It interferes with FindMissingBlobs, and doesn't help Bootstrapping.
            packIndexer.Start();

            downloader.WaitForCompletion();
            this.HasFailures |= downloader.HasFailures;

            packIndexer.WaitForCompletion();
            this.HasFailures |= packIndexer.HasFailures;

            // Since pack indexer is the last to finish before checkout finishes, it should propagate completion.
            // This prevents availableObjects from completing before packIndexer can push its objects through this link.
            checkout.AvailableBlobShas.CompleteAdding();
            checkout.WaitForCompletion();
            this.HasFailures |= checkout.HasFailures;

            if (!this.SkipConfigUpdate && !this.HasFailures)
            {
                this.UpdateRefs(branchOrCommit, isBranch, refs);

                if (isBranch)
                {
                    // Update the refspec before setting the upstream or git will complain the remote branch doesn't exist
                    this.HasFailures |= !this.UpdateRefSpec(this.Tracer, this.Enlistment, branchOrCommit, refs);

                    using (ITracer activity = this.Tracer.StartActivity("SetUpstream", EventLevel.Informational))
                    {
                        string            remoteBranch = refs.GetBranchRefPairs().Single().Key;
                        GitProcess        git          = new GitProcess(this.Enlistment);
                        GitProcess.Result result       = git.SetUpstream(branchOrCommit, remoteBranch);
                        if (result.HasErrors)
                        {
                            activity.RelatedError("Could not set upstream for {0} to {1}: {2}", branchOrCommit, remoteBranch, result.Errors);
                            this.HasFailures = true;
                        }
                    }
                }

                bool shouldSignIndex = !this.GetIsIndexSigningOff();

                // Update the index
                EventMetadata updateIndexMetadata = new EventMetadata();
                updateIndexMetadata.Add("IndexSigningIsOff", shouldSignIndex);
                using (ITracer activity = this.Tracer.StartActivity("UpdateIndex", EventLevel.Informational, Keywords.Telemetry, updateIndexMetadata))
                {
                    Index             sourceIndex = this.GetSourceIndex();
                    GitIndexGenerator indexGen    = new GitIndexGenerator(this.Tracer, this.Enlistment, shouldSignIndex);
                    indexGen.CreateFromHeadTree(indexVersion: 2);
                    this.HasFailures |= indexGen.HasFailures;

                    if (!indexGen.HasFailures)
                    {
                        Index newIndex = new Index(
                            this.Enlistment.EnlistmentRoot,
                            this.Tracer,
                            Path.Combine(this.Enlistment.DotGitRoot, GVFSConstants.DotGit.IndexName),
                            readOnly: false);

                        // Update from disk only if the caller says it is ok via command line
                        // or if we updated the whole tree and know that all files are up to date
                        bool allowIndexMetadataUpdateFromWorkingTree = this.allowIndexMetadataUpdateFromWorkingTree || checkout.UpdatedWholeTree;
                        newIndex.UpdateFileSizesAndTimes(checkout.AddedOrEditedLocalFiles, allowIndexMetadataUpdateFromWorkingTree, shouldSignIndex, sourceIndex);
                    }
                }
            }
        }
Beispiel #3
0
        private bool UpdateFileInformationForAllEntries(MemoryMappedViewAccessor indexView, Index otherIndex, bool shouldAlsoTryPopulateFromDisk)
        {
            long updatedEntriesFromOtherIndex = 0;
            long updatedEntriesFromDisk       = 0;

            using (MemoryMappedFile mmf = otherIndex.GetMemoryMappedFile())
                using (MemoryMappedViewAccessor otherIndexView = mmf.CreateViewAccessor())
                {
                    Parallel.ForEach(
                        this.indexEntryOffsets,
                        entry =>
                    {
                        string currentIndexFilename = entry.Key;
                        long currentIndexOffset     = entry.Value;
                        if (!IndexEntry.HasInitializedCTimeEntry(indexView, currentIndexOffset))
                        {
                            long otherIndexOffset;
                            if (otherIndex.indexEntryOffsets.TryGetValue(currentIndexFilename, out otherIndexOffset))
                            {
                                if (IndexEntry.HasInitializedCTimeEntry(otherIndexView, otherIndexOffset))
                                {
                                    IndexEntry currentIndexEntry = new IndexEntry(indexView, currentIndexOffset);
                                    IndexEntry otherIndexEntry   = new IndexEntry(otherIndexView, otherIndexOffset);

                                    currentIndexEntry.CtimeSeconds            = otherIndexEntry.CtimeSeconds;
                                    currentIndexEntry.CtimeNanosecondFraction = otherIndexEntry.CtimeNanosecondFraction;
                                    currentIndexEntry.MtimeSeconds            = otherIndexEntry.MtimeSeconds;
                                    currentIndexEntry.MtimeNanosecondFraction = otherIndexEntry.MtimeNanosecondFraction;
                                    currentIndexEntry.Dev  = otherIndexEntry.Dev;
                                    currentIndexEntry.Ino  = otherIndexEntry.Ino;
                                    currentIndexEntry.Uid  = otherIndexEntry.Uid;
                                    currentIndexEntry.Gid  = otherIndexEntry.Gid;
                                    currentIndexEntry.Size = otherIndexEntry.Size;

                                    Interlocked.Increment(ref updatedEntriesFromOtherIndex);
                                }
                            }
                            else if (shouldAlsoTryPopulateFromDisk)
                            {
                                string localPath = FromGitRelativePathToDotnetFullPath(currentIndexFilename, this.repoRoot);

                                if (NativeMethods.TryStatFileAndUpdateIndex(this.tracer, localPath, indexView, entry.Value))
                                {
                                    Interlocked.Increment(ref updatedEntriesFromDisk);
                                }
                            }
                        }
                    });
                }

            this.tracer.RelatedEvent(
                EventLevel.Informational,
                "UpdateIndexFileInformation",
                new EventMetadata()
            {
                { "UpdatedFromOtherIndex", updatedEntriesFromOtherIndex },
                { "UpdatedFromDisk", updatedEntriesFromDisk }
            },
                Keywords.Telemetry);

            return((updatedEntriesFromOtherIndex > 0) || (updatedEntriesFromDisk > 0));
        }