public void CanReplaceAnExistingTreeWithAGitLink()
        {
            var          commitId   = (ObjectId)"480095882d281ed676fe5b863569520e54a7d5c0";
            const string targetPath = "just_a_dir";

            var path = SandboxSubmoduleTestRepo();

            using (var repo = new Repository(path))
            {
                TreeDefinition td = TreeDefinition.From(repo.Head.Tip.Tree);
                Assert.Equal(TreeEntryTargetType.Tree, td[targetPath].TargetType);

                Assert.NotNull(td["just_a_dir/contents"]);

                td.AddGitLink(targetPath, commitId);

                TreeEntryDefinition fetched = td[targetPath];
                Assert.NotNull(fetched);

                Assert.Equal(commitId, fetched.TargetId);
                Assert.Equal(TreeEntryTargetType.GitLink, fetched.TargetType);
                Assert.Equal(Mode.GitLink, fetched.Mode);

                Assert.Null(td["just_a_dir/contents"]);
            }
        }
Пример #2
0
 private static void CopyLink(TreeDefinition definition, string name, GitLink link)
 {
     definition.AddGitLink(name, link.Id);
 }
Пример #3
0
        private static IEnumerable <Tuple <ObjectId, ObjectId> > SplitRepository(Repository baseRepo, ICommitLog commits, string name, string path, IEnumerable <SharedRepo> sharedRepos, IEnumerable <MergedRepo> mergedRepos)
        {
            var shared = sharedRepos.Where(r => r.Paths.Any(p => p.StartsWith(path + "/"))).Select(r => new
            {
                Repo    = "../" + r.Name + ".git",
                Path    = r.Paths.First(p => p.StartsWith(path + "/")).Substring(path.Length + 1),
                Commits = r.Commits
            });
            var merged = mergedRepos.Where(r => r.Name == name);

            using (var repo = new Repository(Repository.Init(Path.Combine("output", name))))
            {
                File.Copy(Path.Combine(Config.Instance.MainRepo, ".gitignore"), Path.Combine("output", name, ".gitignore"));
                repo.Index.Add(".gitignore");
                File.Copy(Path.Combine(Config.Instance.MainRepo, ".gitattributes"), Path.Combine("output", name, ".gitattributes"));
                repo.Index.Add(".gitattributes");

                foreach (var toMerge in merged)
                {
                    using (var mergeRepo = new Repository(toMerge.Repo))
                    {
                        foreach (var c in mergeRepo.Commits.QueryBy(new CommitFilter {
                            SortBy = CommitSortStrategies.Topological | CommitSortStrategies.Reverse
                        }))
                        {
                            bool any = false;

                            foreach (var m in toMerge.Mapping)
                            {
                                if (Merge(repo, c, m.Key, m.Value))
                                {
                                    any = true;
                                }
                            }

                            if (any)
                            {
                                var rewrittenCommit = repo.Commit(c.Message, new Signature(c.Author.Name, Config.Instance.MapEmail(c.Author.Email), c.Author.When), new Signature(c.Committer.Name, Config.Instance.MapEmail(c.Author.Email), c.Author.When), new CommitOptions {
                                    AllowEmptyCommit = true
                                });
                                yield return(new Tuple <ObjectId, ObjectId>(c.Id, rewrittenCommit.Id));
                            }
                        }
                    }
                    repo.Index.Clear();
                    repo.Index.Add(".gitignore");
                    repo.Index.Add(".gitattributes");
                }

                if (shared.Any())
                {
                    File.WriteAllText(Path.Combine("output", name, ".gitmodules"), string.Join("", shared.Select(s => $"[submodule \"{s.Path}\"]\n    path = {s.Path}\n    url = {s.Repo}\n")));
                    repo.Index.Add(".gitmodules");
                }

                foreach (var c in commits)
                {
                    if (c.Tree[path] == null)
                    {
                        continue;
                    }

                    TreeDefinition tree = null;
                    foreach (var s in shared)
                    {
                        var mapping = s.Commits.FirstOrDefault(commit => c.Id == commit.Item1);
                        if (mapping != null)
                        {
                            if (tree == null)
                            {
                                var tempCommit = repo.Commit("temp", new Signature("temp", "*****@*****.**", DateTimeOffset.UtcNow), new Signature("temp", "*****@*****.**", DateTimeOffset.UtcNow), new CommitOptions {
                                    AllowEmptyCommit = true
                                });
                                tree = TreeDefinition.From(tempCommit);
                                repo.Reset(ResetMode.Soft, tempCommit.Parents.First());
                            }
                            tree.AddGitLink(s.Path, mapping.Item2);
                        }
                    }
                    if (tree != null)
                    {
                        repo.Index.Replace(repo.ObjectDatabase.CreateTree(tree));
                    }

                    var oldTree = c.Parents.FirstOrDefault()?.Tree?[path]?.Target as Tree;
                    if (oldTree != null)
                    {
                        var diff = baseRepo.Diff.Compare <TreeChanges>(oldTree, (Tree)c.Tree[path].Target);
                        if (!diff.Any())
                        {
                            continue;
                        }
                        foreach (var file in diff)
                        {
                            if (file.Mode == Mode.Directory)
                            {
                                continue;
                            }

                            if (!file.Exists || (file.OldPath != file.Path && file.Status != ChangeKind.Copied))
                            {
                                if (!shared.Any(s => file.OldPath.Replace('\\', '/').StartsWith(s.Path + '/')))
                                {
                                    File.Delete(Path.Combine("output", name, file.OldPath));
                                    repo.Index.Remove(file.OldPath);
                                }
                            }

                            if (file.Exists)
                            {
                                if (!shared.Any(s => file.Path.Replace('\\', '/').StartsWith(s.Path + '/')))
                                {
                                    Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine("output", name, file.Path)));
                                    using (var input = baseRepo.Lookup <Blob>(file.Oid).GetContentStream())
                                        using (var output = File.Create(Path.Combine("output", name, file.Path)))
                                        {
                                            input.CopyTo(output);
                                        }
                                    repo.Index.Add(file.Path);
                                }
                            }
                        }
                    }
                    else
                    {
                        foreach (var entry in RecursiveTree((Tree)c.Tree[path].Target, path))
                        {
                            if (shared.Any(s => entry.Item1.Replace('\\', '/').StartsWith(s.Path + '/')))
                            {
                                continue;
                            }
                            var fullPath = Path.Combine("output", name, entry.Item1);
                            Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
                            using (var content = entry.Item2.GetContentStream())
                                using (var output = File.Create(fullPath))
                                {
                                    content.CopyTo(output);
                                }
                            repo.Index.Add(entry.Item1);
                        }
                    }

                    var email           = Config.Instance.MapEmail(c.Author.Email);
                    var rewrittenCommit = repo.Commit(c.Message, new Signature(c.Author.Name, email, c.Author.When), new Signature(c.Committer.Name, email, c.Author.When), new CommitOptions {
                        AllowEmptyCommit = true
                    });
                    yield return(new Tuple <ObjectId, ObjectId>(c.Id, rewrittenCommit.Id));
                }

                repo.Network.Remotes.Add("origin", Config.Instance.Origin(name));

                Console.WriteLine("Copying LFS files...");
                var lfsCount = CopyLfsFiles(repo, Path.Combine("output", name, ".git", "lfs", "objects"), new[] { Path.Combine(baseRepo.Info.WorkingDirectory, ".git", "lfs", "objects") }.Concat(merged.Where(r => r.Name == name).Select(r => Path.Combine(r.Repo, ".git", "lfs", "objects"))));
                Console.WriteLine($"Copied {lfsCount} files.");

                // LibGit2Sharp doesn't support git gc, so we use the command line:
                using (var gc = Process.Start(new ProcessStartInfo("git", "gc --aggressive")
                {
                    WorkingDirectory = repo.Info.WorkingDirectory,
                    UseShellExecute = false
                }))
                {
                    gc.WaitForExit();
                }
            }
        }