コード例 #1
0
 private ArgumentBuilder BuildArguments(RefFilterOptions refFilterOptions,
                                        string branchFilter,
                                        string revisionFilter,
                                        string pathFilter)
 {
     return(new GitArgumentBuilder("log")
     {
         "-z",
         branchFilter,
         $"--pretty=format:\"{FullFormat}\"",
         {
             refFilterOptions.HasFlag(RefFilterOptions.FirstParent),
             "--first-parent",
             new ArgumentBuilder
             {
                 { refFilterOptions.HasFlag(RefFilterOptions.Reflogs), "--reflog" },
                 { AppSettings.SortByAuthorDate, "--author-date-order" },
                 {
                     refFilterOptions.HasFlag(RefFilterOptions.All),
                     "--all",
                     new ArgumentBuilder
                     {
                         {
                             refFilterOptions.HasFlag(RefFilterOptions.Branches) &&
                             !string.IsNullOrWhiteSpace(branchFilter) && branchFilter.IndexOfAny(new[] { '?', '*', '[' }) != -1,
                             "--branches=" + branchFilter
                         },
                         { refFilterOptions.HasFlag(RefFilterOptions.Remotes), "--remotes" },
                         { refFilterOptions.HasFlag(RefFilterOptions.Tags), "--tags" },
                     }.ToString()
                 },
                 { refFilterOptions.HasFlag(RefFilterOptions.Boundary), "--boundary" },
                 { refFilterOptions.HasFlag(RefFilterOptions.ShowGitNotes), "--not --glob=notes --not" },
                 { refFilterOptions.HasFlag(RefFilterOptions.NoMerges), "--no-merges" },
                 { refFilterOptions.HasFlag(RefFilterOptions.SimplifyByDecoration), "--simplify-by-decoration" }
             }.ToString()
         },
         revisionFilter,
         "--",
         pathFilter
     });
 }
コード例 #2
0
        private async Task ExecuteAsync(
            GitModule module,
            IObserver <GitRevision> subject,
            RefFilterOptions refFilterOptions,
            string branchFilter,
            string revisionFilter,
            string pathFilter,
            [CanBeNull] Func <GitRevision, bool> revisionPredicate)
        {
            ThreadHelper.ThrowIfNotOnUIThread();

            var token = _cancellationTokenSequence.Next();

            RevisionCount = 0;

            await TaskScheduler.Default;

            subject.OnNext(null);

            token.ThrowIfCancellationRequested();

            var branchName = module.IsValidGitWorkingDir()
                ? module.GetSelectedBranch()
                : "";

            token.ThrowIfCancellationRequested();

            LatestRefs = module.GetRefs();
            UpdateSelectedRef(module, LatestRefs, branchName);
            var refsByObjectId = LatestRefs.ToLookup(head => head.Guid);

            token.ThrowIfCancellationRequested();

            const string fullFormat =

                // These header entries can all be decoded from the bytes directly.
                // Each hash is 20 bytes long. There is always a

                /* Object ID       */ "%H" +
                /* Tree ID         */ "%T" +
                /* Parent IDs      */ "%P%n" +
                /* Author date     */ "%at%n" +
                /* Commit date     */ "%ct%n" +
                /* Encoding        */ "%e%n" +

                // Items below here must be decoded as strings to support non-ASCII

                /* Author name     */ "%aN%n" +
                /* Author email    */ "%aE%n" +
                /* Committer name  */ "%cN%n" +
                /* Committer email */ "%cE%n" +
                /* Commit subject  */ "%s%n%n" +
                /* Commit body     */ "%b";

            // TODO add AppBuilderExtensions support for flags enums, starting with RefFilterOptions, then use it in the below construction

            var arguments = BuildArguments();

            var sw = Stopwatch.StartNew();

            // This property is relatively expensive to call for every revision, so
            // cache it for the duration of the loop.
            var logOutputEncoding = module.LogOutputEncoding;

            using (var process = module.RunGitCmdDetached(arguments.ToString(), GitModule.LosslessEncoding))
            {
                token.ThrowIfCancellationRequested();

                // Pool string values likely to form a small set: encoding, authorname, authoremail, committername, committeremail
                var stringPool = new StringPool();

                var buffer = new byte[4096];

                foreach (var chunk in process.StandardOutput.BaseStream.ReadNullTerminatedChunks(ref buffer))
                {
                    token.ThrowIfCancellationRequested();

                    if (TryParseRevision(module, chunk, stringPool, logOutputEncoding, out var revision))
                    {
                        if (revisionPredicate == null || revisionPredicate(revision))
                        {
                            // Remove full commit message to reduce memory consumption (28% for a repo with 69K commits)
                            // Full commit message is used in InMemFilter but later it's not needed
                            revision.Body = null;

                            // Look up any refs associate with this revision
                            revision.Refs = refsByObjectId[revision.Guid].AsReadOnlyList();

                            RevisionCount++;

                            subject.OnNext(revision);
                        }
                    }
                }

                Trace.WriteLine($"**** [{nameof(RevisionReader)}] Emitted {RevisionCount} revisions in {sw.Elapsed.TotalMilliseconds:#,##0.#} ms. bufferSize={buffer.Length} poolCount={stringPool.Count}");
            }

            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(token);

            if (!token.IsCancellationRequested)
            {
                subject.OnCompleted();
            }

            ArgumentBuilder BuildArguments()
            {
                return(new ArgumentBuilder
                {
                    "log",
                    "-z",
                    $"--pretty=format:\"{fullFormat}\"",
                    { AppSettings.OrderRevisionByDate, "--date-order", "--topo-order" },
                    { AppSettings.ShowReflogReferences, "--reflog" },
                    {
                        refFilterOptions.HasFlag(RefFilterOptions.All),
                        "--all",
                        new ArgumentBuilder
                        {
                            {
                                refFilterOptions.HasFlag(RefFilterOptions.Branches) &&
                                !branchFilter.IsNullOrWhiteSpace() &&
                                branchFilter.IndexOfAny(new[] { '?', '*', '[' }) != -1,
                                "--branches=" + branchFilter
                            },
                            { refFilterOptions.HasFlag(RefFilterOptions.Remotes), "--remotes" },
                            { refFilterOptions.HasFlag(RefFilterOptions.Tags), "--tags" },
                        }.ToString()
                    },
                    { refFilterOptions.HasFlag(RefFilterOptions.Boundary), "--boundary" },
                    { refFilterOptions.HasFlag(RefFilterOptions.ShowGitNotes), "--not --glob=notes --not" },
                    { refFilterOptions.HasFlag(RefFilterOptions.NoMerges), "--no-merges" },
                    { refFilterOptions.HasFlag(RefFilterOptions.FirstParent), "--first-parent" },
                    { refFilterOptions.HasFlag(RefFilterOptions.SimplifyByDecoration), "--simplify-by-decoration" },
                    revisionFilter,
                    "--",
                    pathFilter
                });
            }
        }
コード例 #3
0
        private async Task ExecuteAsync(
            GitModule module,
            IReadOnlyList <IGitRef> refs,
            IObserver <GitRevision> subject,
            RefFilterOptions refFilterOptions,
            string branchFilter,
            string revisionFilter,
            string pathFilter,
            [CanBeNull] Func <GitRevision, bool> revisionPredicate)
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

            var token = _cancellationTokenSequence.Next();

            var revisionCount = 0;

            await TaskScheduler.Default;

            token.ThrowIfCancellationRequested();

            var branchName = module.IsValidGitWorkingDir()
                ? module.GetSelectedBranch()
                : "";

            token.ThrowIfCancellationRequested();

            UpdateSelectedRef(module, refs, branchName);
            var refsByObjectId = refs.ToLookup(head => head.ObjectId);

            token.ThrowIfCancellationRequested();

            const string fullFormat =

                // These header entries can all be decoded from the bytes directly.
                // Each hash is 20 bytes long.

                /* Object ID       */ "%H" +
                /* Tree ID         */ "%T" +
                /* Parent IDs      */ "%P%n" +
                /* Author date     */ "%at%n" +
                /* Commit date     */ "%ct%n" +
                /* Encoding        */ "%e%n" +

                // Items below here must be decoded as strings to support non-ASCII.

                /* Author name     */ "%aN%n" +
                /* Author email    */ "%aE%n" +
                /* Committer name  */ "%cN%n" +
                /* Committer email */ "%cE%n" +
                /* Commit subject  */ "%s%n%n" +
                /* Commit body     */ "%b";

            var arguments = BuildArguments();

            var sw = Stopwatch.StartNew();

            // This property is relatively expensive to call for every revision, so
            // cache it for the duration of the loop.
            var logOutputEncoding = module.LogOutputEncoding;

            using (var process = module.RunGitCmdDetached(arguments, redirectOutput: true, outputEncoding: GitModule.LosslessEncoding))
            {
                token.ThrowIfCancellationRequested();

                // Pool string values likely to form a small set: encoding, authorname, authoremail, committername, committeremail
                var stringPool = new StringPool();

                var buffer = new byte[4096];

                foreach (var chunk in process.StandardOutput.BaseStream.ReadNullTerminatedChunks(ref buffer))
                {
                    token.ThrowIfCancellationRequested();

                    if (TryParseRevision(module, chunk, stringPool, logOutputEncoding, out var revision))
                    {
                        if (revisionPredicate == null || revisionPredicate(revision))
                        {
                            // The full commit message body is used initially in InMemFilter, after which it isn't
                            // strictly needed and can be re-populated asynchronously.
                            //
                            // We keep full multiline message bodies within the last six months.
                            // Commits earlier than that have their properties set to null and the
                            // memory will be GCd.
                            if (DateTime.Now - revision.AuthorDate > TimeSpan.FromDays(30 * 6))
                            {
                                revision.Body = null;
                            }

                            // Look up any refs associated with this revision
                            revision.Refs = refsByObjectId[revision.ObjectId].AsReadOnlyList();

                            revisionCount++;

                            subject.OnNext(revision);
                        }
                    }
                }

                Trace.WriteLine($"**** [{nameof(RevisionReader)}] Emitted {revisionCount} revisions in {sw.Elapsed.TotalMilliseconds:#,##0.#} ms. bufferSize={buffer.Length} poolCount={stringPool.Count}");
            }

            if (!token.IsCancellationRequested)
            {
                subject.OnCompleted();
            }

            ArgumentBuilder BuildArguments()
            {
                return(new GitArgumentBuilder("log")
                {
                    "-z",
                    $"--pretty=format:\"{fullFormat}\"",
                    { AppSettings.ShowReflogReferences, "--reflog" },
                    {
                        refFilterOptions.HasFlag(RefFilterOptions.All),
                        "--all",
                        new ArgumentBuilder
                        {
                            {
                                refFilterOptions.HasFlag(RefFilterOptions.Branches) &&
                                !branchFilter.IsNullOrWhiteSpace() &&
                                branchFilter.IndexOfAny(new[] { '?', '*', '[' }) != -1,
                                "--branches=" + branchFilter
                            },
                            { refFilterOptions.HasFlag(RefFilterOptions.Remotes), "--remotes" },
                            { refFilterOptions.HasFlag(RefFilterOptions.Tags), "--tags" },
                        }.ToString()
                    },
                    { refFilterOptions.HasFlag(RefFilterOptions.Boundary), "--boundary" },
                    { refFilterOptions.HasFlag(RefFilterOptions.ShowGitNotes), "--not --glob=notes --not" },
                    { refFilterOptions.HasFlag(RefFilterOptions.NoMerges), "--no-merges" },
                    { refFilterOptions.HasFlag(RefFilterOptions.FirstParent), "--first-parent" },
                    { refFilterOptions.HasFlag(RefFilterOptions.SimplifyByDecoration), "--simplify-by-decoration" },
                    revisionFilter,
                    "--",
                    pathFilter
                });
            }
        }