Beispiel #1
0
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
        public override async IAsyncEnumerable <GitReference> GetAll(HashSet <string> alreadyReturned)
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
        {
            string baseDir = Path.GetFullPath(GitDir);

            foreach (string file in Directory.EnumerateFiles(Path.Combine(baseDir, "refs"), "*", SearchOption.AllDirectories))
            {
                if (file.Length > baseDir.Length + 1 && file[baseDir.Length] == Path.DirectorySeparatorChar)
                {
                    string name = file.Substring(baseDir.Length + 1).Replace(Path.DirectorySeparatorChar, '/');

                    yield return(new GitReference(this, name, (GitId?)null));
                }
            }

            foreach (string file in Directory.EnumerateFiles(GitDir))
            {
                string name = Path.GetFileName(file);

                if (GitReference.AllUpper(name) && !alreadyReturned.Contains(name))
                {
                    yield return(new GitSymbolicReference(this, file.Substring(GitDir.Length + 1)));
                }
            }
        }
Beispiel #2
0
        private async ValueTask CommitUpdateFileReferences()
        {
            foreach (var v in Updates.Where(x => x.Type == UpdateType.Verify))
            {
                var r = await _referenceRepository.GetAsync(v.Name).ConfigureAwait(false);

                if (r is null || r.Id != v.Id)
                {
                    throw new GitException($"Reference {v.Name} is not {v.Id}, but {r?.Id ?? Zero}");
                }
            }

            Action?unlock        = null;
            string?hookData      = null;
            bool?  logRefUpdates = null;

            try
            {
                foreach (var v in Updates.Select(x => x.Name).Distinct())
                {
                    var name = v;
                    if (GitReference.AllUpper(name))
                    {
                        var rf = await _referenceRepository.GetAsync(v).ConfigureAwait(false);

                        if (rf is GitSymbolicReference sr)
                        {
                            rf = await sr.ResolveAsync().ConfigureAwait(false);

                            name = (rf as GitSymbolicReference)?.ReferenceName ?? rf.Name ?? name;
                        }
                    }

                    string path = Path.Combine(Repository.GitDirectory, name);
                    Directory.CreateDirectory(Path.GetDirectoryName(path) !);

                    string p = path + ".lock";
#pragma warning disable CA2000 // Dispose objects before losing scope
                    var f = new FileStream(path + ".lock", FileMode.CreateNew);
#pragma warning restore CA2000 // Dispose objects before losing scope

                    unlock += () => { f.Close(); File.Delete(p); };
                }


                bool allowContinue = true;

                if (!string.IsNullOrEmpty(GitConfiguration.GitProgramPath) &&
                    await Repository.Configuration.HookExistsAsync("reference-transaction").ConfigureAwait(false))
                {
                    StringBuilder sb = new StringBuilder();

                    foreach (var v in Updates)
                    {
                        GitReference?rf;

                        // We might record 'HEAD' when we really update something like 'refs/heads/main'
                        // This might need fixing when things are fixed in git itself
                        switch (v.Type)
                        {
                        case UpdateType.Create:
                            sb.Append(Zero);
                            sb.Append(' ');
                            sb.Append(v.Id);
                            sb.Append(' ');
                            sb.Append(v.Name);
                            sb.Append('\n');
                            break;

                        case UpdateType.Update:
                            rf = await _referenceRepository.GetAsync(v.Name).ConfigureAwait(false);

                            sb.Append(rf?.Id ?? Zero);
                            sb.Append(' ');
                            sb.Append(v.Id);
                            sb.Append(' ');
                            sb.Append(v.Name);
                            sb.Append('\n');
                            break;

                        case UpdateType.Delete:
                            rf = await _referenceRepository.GetAsync(v.Name).ConfigureAwait(false);

                            if (rf?.Id != null)
                            {
                                sb.Append(rf.Id);
                                sb.Append(' ');
                                sb.Append(Zero);
                                sb.Append(' ');
                                sb.Append(v.Name);
                                sb.Append('\n');
                            }
                            break;
                        }
                    }

                    if (sb.Length > 0)
                    {
                        hookData = sb.ToString();
                    }

                    if (hookData is not null)
                    {
                        var r = await Repository.RunHookErrAsync("reference-transaction", new[] { "prepared" }, stdinText : hookData, expectedResults : Array.Empty <int>()).ConfigureAwait(false);

                        if (r.ExitCode != 0)
                        {
                            throw new GitException($"Git reference-transaction denied update: {r.OutputText} ({r.ErrorText})");
                        }
                    }
                }

                if (allowContinue)
                {
                    var signature = Repository.Configuration.Identity.AsRecord();
                    foreach (var v in Updates)
                    {
                        GitId?       originalId = null;
                        GitReference?rf         = null;
                        switch (v.Type)
                        {
                        case UpdateType.Create:
                            using (var fs = new FileStream(Path.Combine(Repository.GitDirectory, v.TargetName), FileMode.CreateNew))
                                using (var sw = new StreamWriter(fs))
                                {
                                    await sw.WriteLineAsync(v.Id !.ToString()).ConfigureAwait(false);
                                }
                            break;

                        case UpdateType.Update:
                            rf = await _referenceRepository.GetAsync(v.Name).ConfigureAwait(false);

                            if (rf is GitSymbolicReference sr)
                            {
                                rf = await sr.ResolveAsync().ConfigureAwait(false);

                                v.TargetName = (rf as GitSymbolicReference)?.ReferenceName ?? rf.Name ?? v.Name;
                            }
                            originalId = rf?.Id;
                            using (var fs = new FileStream(Path.Combine(Repository.GitDirectory, v.TargetName), FileMode.Create))
                                using (var sw = new StreamWriter(fs))
                                {
                                    fs.SetLength(0);
                                    await sw.WriteLineAsync(v.Id !.ToString()).ConfigureAwait(false);
                                }
                            break;

                        case UpdateType.Delete:
                            rf = await _referenceRepository.GetAsync(v.Name).ConfigureAwait(false);

                            if (rf is GitSymbolicReference sr2)
                            {
                                rf = await sr2.ResolveAsync().ConfigureAwait(false);

                                v.TargetName = rf.Name ?? v.Name;
                            }
                            originalId = rf?.Id;
                            File.Delete(Path.Combine(Repository.GitDirectory, v.TargetName));
                            // If failed here, we need to cleanup packed references!!
                            break;

                        default:
                            continue;
                        }

                        logRefUpdates ??= await Repository.Configuration.GetBoolAsync("core", "logallrefupdates").ConfigureAwait(false) ?? false;

                        if (logRefUpdates == true &&
                            (GitReference.AllUpper(v.Name) || v.Name.StartsWith("refs/heads/", StringComparison.OrdinalIgnoreCase) ||
                             v.Name.StartsWith("refs/remotes/", StringComparison.OrdinalIgnoreCase) || v.Name.StartsWith("refs/notes/", StringComparison.OrdinalIgnoreCase)))
                        {
                            var log = new GitReferenceLogRecord {
                                Original = originalId ?? Zero, Target = v.Id ?? Zero, Signature = signature, Reason = Reason
                            };

                            await AppendLog(v.Name, log).ConfigureAwait(false);

                            if (rf is not null && rf.Name != v.Name)
                            {
                                await AppendLog(rf.Name !, log).ConfigureAwait(false);
                            }
                        }
                    }

                    if (hookData is not null)
                    {
                        var hd = hookData;
                        hookData = null;
                        // Ignore errors
                        await Repository.RunHookErrAsync("run", new[] { "committed" }, stdinText : hd, expectedResults : Array.Empty <int>()).ConfigureAwait(false);
                    }
                }
            }
            catch when(hookData is not null)
            {
                // Ignore errors
                await Repository.RunHookErrAsync("run", new[] { "abort" }, stdinText : hookData, expectedResults : Array.Empty <int>()).ConfigureAwait(false);

                throw;
            }
            finally
            {
                unlock?.Invoke();
            }
        }