From afce64c411a358cf8e642fe3fb4462370002e21c Mon Sep 17 00:00:00 2001 From: Julien Richard Date: Sat, 29 Jul 2017 13:56:44 +0200 Subject: [PATCH 01/13] Add IndexAllDepotFiles option (-a) It allows to properly index sources for native Pdbs --- src/GitLink/LinkOptions.cs | 2 + src/GitLink/Linker.cs | 79 +++++++++++++++++++++++++------------- src/GitLink/Program.cs | 3 ++ 3 files changed, 58 insertions(+), 26 deletions(-) diff --git a/src/GitLink/LinkOptions.cs b/src/GitLink/LinkOptions.cs index 145ade8..6b09f1e 100644 --- a/src/GitLink/LinkOptions.cs +++ b/src/GitLink/LinkOptions.cs @@ -23,5 +23,7 @@ public struct LinkOptions public string CommitId { get; set; } public string GitWorkingDirectory { get; set; } + + public bool IndexAllDepotFiles { get; set; } } } diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index 28dc0e5..7ae8106 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -37,30 +37,50 @@ public static class Linker var projectSrcSrvFile = pdbPath + ".srcsrv"; string repositoryDirectory; - IReadOnlyCollection sourceFiles; + IEnumerable sourceFiles; IReadOnlyDictionary repoSourceFiles; + using (var pdb = new PdbFile(pdbPath)) { - sourceFiles = pdb.GetFilesAndChecksums().Keys.ToList(); - - if (options.GitWorkingDirectory != null) + if (options.IndexAllDepotFiles) { - repositoryDirectory = Path.Combine(options.GitWorkingDirectory, ".git"); + repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(pdbPath)); + sourceFiles = GetSourceFilesFromDepot(repositoryDirectory); } else { - string someSourceFile = sourceFiles.FirstOrDefault(); - if (someSourceFile == null) + sourceFiles = pdb.GetFilesAndChecksums().Keys.ToList(); + + if (options.GitWorkingDirectory != null) { - Log.Error("No source files were found in the PDB."); - return false; + repositoryDirectory = Path.Combine(options.GitWorkingDirectory, ".git"); } - - repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(someSourceFile)); - if (repositoryDirectory == null) + else { - Log.Error("No source files found that are tracked in a git repo."); - return false; + string someSourceFile = sourceFiles.FirstOrDefault(); + if (someSourceFile == null) + { + Log.Error("No source files were found in the PDB."); + return false; + } + + repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(someSourceFile)); + if (repositoryDirectory == null) + { + Log.Error("No source files found that are tracked in a git repo."); + return false; + } + + if (!options.SkipVerify) + { + Log.Debug("Verifying pdb file"); + + var missingFiles = pdb.FindMissingOrChangedSourceFiles(); + foreach (var missingFile in missingFiles) + { + Log.Warning($"File \"{missingFile}\" missing or changed since the PDB was compiled."); + } + } } } @@ -109,17 +129,6 @@ public static class Linker repoSourceFiles = sourceFiles.ToDictionary(e => e, e => GetNormalizedPath(e, workingDirectory)); } - if (!options.SkipVerify) - { - Log.Debug("Verifying pdb file"); - - var missingFiles = pdb.FindMissingOrChangedSourceFiles(); - foreach (var missingFile in missingFiles) - { - Log.Warning($"File \"{missingFile}\" missing or changed since the PDB was compiled."); - } - } - string rawUrl = provider.RawGitUrl; if (rawUrl.Contains(RevisionPlaceholder) || rawUrl.Contains(FilenamePlaceholder)) { @@ -182,11 +191,29 @@ public static class Linker Log.Debug("Created source server link file, updating pdb file \"{0}\"", Catel.IO.Path.GetRelativePath(pdbPath, repositoryDirectory)); PdbStrHelper.Execute(PdbStrExePath, pdbPath, projectSrcSrvFile); var indexedFilesCount = repoSourceFiles.Values.Count(v => v != null); - Log.Info($"Remote git source information for {indexedFilesCount}/{sourceFiles.Count} files written to pdb: \"{pdbPath}\""); + Log.Info($"Remote git source information for {indexedFilesCount}/{sourceFiles.Count()} files written to pdb: \"{pdbPath}\""); return true; } + private static IEnumerable GetSourceFilesFromDepot(string repositoryDirectory) + { + IEnumerable sourceFiles; + var repo = new Repository(repositoryDirectory); + { + sourceFiles = from file in Directory.GetFiles(repo.Info.WorkingDirectory, "*.*", SearchOption.AllDirectories) + where !repo.Ignore.IsPathIgnored(file) + let ext = Path.GetExtension(file) + where string.Equals(ext, ".cs", StringComparison.OrdinalIgnoreCase) + || string.Equals(ext, ".cpp", StringComparison.OrdinalIgnoreCase) + || string.Equals(ext, ".c", StringComparison.OrdinalIgnoreCase) + || string.Equals(ext, ".h", StringComparison.OrdinalIgnoreCase) + select file; + } + + return sourceFiles; + } + private static void CreateSrcSrv(string srcsrvFile, SrcSrvContext srcSrvContext) { Argument.IsNotNull(nameof(srcSrvContext), srcSrvContext); diff --git a/src/GitLink/Program.cs b/src/GitLink/Program.cs index e8ff243..d9b1585 100644 --- a/src/GitLink/Program.cs +++ b/src/GitLink/Program.cs @@ -31,6 +31,7 @@ private static int Main(string[] args) string baseDir = null; string pdbPath = null; bool skipVerify = false; + bool allDepotFiles = false; LinkMethod method = LinkMethod.Http; var arguments = ArgumentSyntax.Parse(args, syntax => { @@ -39,6 +40,7 @@ private static int Main(string[] args) syntax.DefineOption("commit", ref commitId, "The git ref to assume all the source code belongs to."); syntax.DefineOption("baseDir", ref baseDir, "The path to the root of the git repo."); syntax.DefineOption("s|skipVerify", ref skipVerify, "Verify all source files are available in source control."); + syntax.DefineOption("a|allDepotFiles", ref allDepotFiles, "Index all source files from depot. Add this option for native PDBs (C++)."); syntax.DefineParameter("pdb", ref pdbPath, "The PDB to add source indexing to."); if (!string.IsNullOrEmpty(pdbPath) && !File.Exists(pdbPath)) @@ -65,6 +67,7 @@ private static int Main(string[] args) CommitId = commitId, SkipVerify = skipVerify, Method = method, + IndexAllDepotFiles = allDepotFiles, }; if (!Linker.Link(pdbPath, options)) From a2cffb801348143afddcd4a677a0a8b9a8ee45a0 Mon Sep 17 00:00:00 2001 From: Julien Richard Date: Sat, 29 Jul 2017 14:12:58 +0200 Subject: [PATCH 02/13] Add IndexAllDepotFiles option in MsBuild task --- src/GitLinkTask/GitLink.targets | 1 + src/GitLinkTask/LinkPdbToGitRemote.cs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/GitLinkTask/GitLink.targets b/src/GitLinkTask/GitLink.targets index 2e25ce8..aff9d5d 100644 --- a/src/GitLinkTask/GitLink.targets +++ b/src/GitLinkTask/GitLink.targets @@ -10,6 +10,7 @@ PdbFile="$(IntermediateOutputPath)$(TargetName).pdb" Method="$(GitLinkMethod)" SkipVerify="$(GitLinkSkipVerify)" + IndexAllDepotFiles="$(GitLinkIndexAllDepotFiles)" GitRemoteUrl="$(GitLinkGitRemoteUrl)" GitWorkingDirectory="$(GitWorkingDirectory)" GitCommitId="$(GitCommitId)" diff --git a/src/GitLinkTask/LinkPdbToGitRemote.cs b/src/GitLinkTask/LinkPdbToGitRemote.cs index 2b4a06c..c79c32f 100644 --- a/src/GitLinkTask/LinkPdbToGitRemote.cs +++ b/src/GitLinkTask/LinkPdbToGitRemote.cs @@ -21,6 +21,8 @@ public string Method public bool SkipVerify { get; set; } + public bool IndexAllDepotFiles { get; set; } + public string GitRemoteUrl { get; set; } public string GitCommitId { get; set; } @@ -40,6 +42,7 @@ public override bool Execute() GitRemoteUrl = GitRemoteUrl != null ? new Uri(GitRemoteUrl, UriKind.Absolute) : null, CommitId = GitCommitId, GitWorkingDirectory = GitWorkingDirectory, + IndexAllDepotFiles = IndexAllDepotFiles, }; bool success = Linker.Link(PdbFile.GetMetadata("FullPath"), options); From a94353dba83e2404329cae64ee7b60d97b9af5a4 Mon Sep 17 00:00:00 2001 From: Julien Richard Date: Sat, 29 Jul 2017 14:16:38 +0200 Subject: [PATCH 03/13] Update user message when no source files is found --- src/GitLink/Linker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index 7ae8106..a74c1b1 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -60,7 +60,7 @@ public static class Linker string someSourceFile = sourceFiles.FirstOrDefault(); if (someSourceFile == null) { - Log.Error("No source files were found in the PDB."); + Log.Error("No source files were found in the PDB. If you're PDB is a native one you should use -a option."); return false; } From 1a9ba3fddaad73a7257598c5a78d7d9c5b04e3ee Mon Sep 17 00:00:00 2001 From: Julien Richard Date: Sat, 29 Jul 2017 14:27:59 +0200 Subject: [PATCH 04/13] Ads a bunch of "official" C++ file extensions See https://stackoverflow.com/questions/1545080/c-code-file-extension-cc-vs-cpp --- src/GitLink/Linker.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index a74c1b1..caa4955 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -207,7 +207,13 @@ private static IEnumerable GetSourceFilesFromDepot(string repositoryDire where string.Equals(ext, ".cs", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".cpp", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".c", StringComparison.OrdinalIgnoreCase) + || string.Equals(ext, ".cc", StringComparison.OrdinalIgnoreCase) + || string.Equals(ext, ".cxx", StringComparison.OrdinalIgnoreCase) + || string.Equals(ext, ".c++", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".h", StringComparison.OrdinalIgnoreCase) + || string.Equals(ext, ".hh", StringComparison.OrdinalIgnoreCase) + || string.Equals(ext, ".inl", StringComparison.OrdinalIgnoreCase) + || string.Equals(ext, ".hpp", StringComparison.OrdinalIgnoreCase) select file; } From a57871131f5e28484fc21a672c3cafcdf3161acd Mon Sep 17 00:00:00 2001 From: Julien Richard Date: Sat, 29 Jul 2017 14:28:26 +0200 Subject: [PATCH 05/13] Add native PDBs section in README file --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 28d2b3d..2db2641 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,10 @@ There are many more parameters you can use. Display the usage doc with the follo GitLink.exe -h +### Native PDBs + +Native PDBs (from C++ projects) are supported by using -a option. All .cpp/.c/.h files from your git depot will be indexed in the PDB. + # How does it work The SrcSrv tool (Srcsrv.dll) enables a client to retrieve the exact version of the source files that were used to build an application. Because the source code for a module can change between versions and over the course of years, it is important to look at the source code as it existed when the version of the module in question was built. From 68ec0fdd1ee0996716a8ca206ad5527972f20d33 Mon Sep 17 00:00:00 2001 From: Julien Richard Date: Sat, 29 Jul 2017 14:35:56 +0200 Subject: [PATCH 06/13] Tweak README a little bit --- README.md | 6 +++++- src/GitLink.sln.DotSettings | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2db2641..594f837 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,11 @@ There are many more parameters you can use. Display the usage doc with the follo ### Native PDBs -Native PDBs (from C++ projects) are supported by using -a option. All .cpp/.c/.h files from your git depot will be indexed in the PDB. +Native PDBs (from C++ projects) are supported by using -a option: + + GitLink.exe -a + +All source code files from your git depot will be indexed in the PDB. # How does it work diff --git a/src/GitLink.sln.DotSettings b/src/GitLink.sln.DotSettings index af92192..f763e30 100644 --- a/src/GitLink.sln.DotSettings +++ b/src/GitLink.sln.DotSettings @@ -28,6 +28,7 @@ False False False + False 20 False @@ -35,6 +36,7 @@ 1 OnSingleLine FirstAttributeOnSingleLine + False <?xml version="1.0" encoding="utf-16"?> <Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"> <TypePattern Priority="100" DisplayName="Type Pattern"> @@ -416,6 +418,7 @@ II.2.12 <HandlesEvent /> True True True + True None <data /> <data><IncludeFilters /><ExcludeFilters /></data> \ No newline at end of file From 8364423a271604ad3f4f1a3363bbe49ee1e330fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sat, 29 Jul 2017 22:05:11 +0300 Subject: [PATCH 07/13] Added ASP.NET Boilerplate to list of projects already using GitLink --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 594f837..c8b3e57 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,7 @@ It is also possible to specify a custom url provider. Below is a list of projects already using GitLink (alphabetically ordered). +- ASP.NET Boilerplate - Catel - eXpand - FakeItEasy From 354f86c7d0af5158011985e5417944753c77dd61 Mon Sep 17 00:00:00 2001 From: Julien Richard Date: Sun, 30 Jul 2017 14:06:33 +0200 Subject: [PATCH 08/13] Clean IndexAllDepotFiles option - make GitWorkingDirectory option work with IndexAllDepotFiles option - added some error messages - encapsulate using (var pdb = new PdbFile(pdbPath)) in a method --- README.md | 2 +- src/GitLink/Linker.cs | 253 ++++++++++++++++++++++-------------------- 2 files changed, 132 insertions(+), 123 deletions(-) diff --git a/README.md b/README.md index 594f837..a7f785b 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ Native PDBs (from C++ projects) are supported by using -a option: GitLink.exe -a -All source code files from your git depot will be indexed in the PDB. +All known C++ source files from your git depot will be indexed in the PDB. # How does it work diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index caa4955..e9541b9 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -36,167 +36,176 @@ public static class Linker Argument.IsNotNullOrEmpty(() => pdbPath); var projectSrcSrvFile = pdbPath + ".srcsrv"; - string repositoryDirectory; - IEnumerable sourceFiles; + string repositoryDirectory = null; + IReadOnlyList sourceFiles; IReadOnlyDictionary repoSourceFiles; - using (var pdb = new PdbFile(pdbPath)) + if (options.GitWorkingDirectory != null) { - if (options.IndexAllDepotFiles) + repositoryDirectory = Path.Combine(options.GitWorkingDirectory, ".git"); + if (!Directory.Exists(repositoryDirectory)) { - repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(pdbPath)); - sourceFiles = GetSourceFilesFromDepot(repositoryDirectory); + Log.Error("Provided directory does not contain a git depot."); + return false; } - else - { - sourceFiles = pdb.GetFilesAndChecksums().Keys.ToList(); + } - if (options.GitWorkingDirectory != null) - { - repositoryDirectory = Path.Combine(options.GitWorkingDirectory, ".git"); - } - else + if (options.IndexAllDepotFiles) + { + if (repositoryDirectory == null) + { + repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(pdbPath)); + if (repositoryDirectory == null) { - string someSourceFile = sourceFiles.FirstOrDefault(); - if (someSourceFile == null) - { - Log.Error("No source files were found in the PDB. If you're PDB is a native one you should use -a option."); - return false; - } - - repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(someSourceFile)); - if (repositoryDirectory == null) - { - Log.Error("No source files found that are tracked in a git repo."); - return false; - } - - if (!options.SkipVerify) - { - Log.Debug("Verifying pdb file"); - - var missingFiles = pdb.FindMissingOrChangedSourceFiles(); - foreach (var missingFile in missingFiles) - { - Log.Warning($"File \"{missingFile}\" missing or changed since the PDB was compiled."); - } - } + Log.Error("Couldn't auto detect git repo. Please use -baseDir to manually set it."); + return false; } } - string workingDirectory = Path.GetDirectoryName(repositoryDirectory); + sourceFiles = GetSourceFilesFromDepot(repositoryDirectory); + } + else + { + sourceFiles = GetSourceFilesFromPdb(pdbPath); + + string someSourceFile = sourceFiles.FirstOrDefault(); + if (someSourceFile == null) + { + Log.Error("No source files were found in the PDB. If you're PDB is a native one you should use -a option."); + return false; + } - var repository = new Lazy(() => new Repository(repositoryDirectory)); - try + if (repositoryDirectory == null) { - string commitId = options.CommitId ?? repository.Value.Head.Commits.FirstOrDefault()?.Sha; - if (commitId == null) + repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(sourceFiles.FirstOrDefault())); + if (repositoryDirectory == null) { - Log.Error("No commit is checked out to HEAD. Have you committed yet?"); + Log.Error("No source files found that are tracked in a git repo."); return false; } + } + } - var providerManager = new Providers.ProviderManager(); - Providers.IProvider provider; - if (options.GitRemoteUrl == null) - { - var candidateProviders = from remote in repository.Value.Network.Remotes - let p = providerManager.GetProvider(remote.Url) - where p != null - select p; - provider = candidateProviders.FirstOrDefault(); - } - else - { - provider = providerManager.GetProvider(options.GitRemoteUrl.AbsoluteUri); - } + string workingDirectory = Path.GetDirectoryName(repositoryDirectory); - if (provider == null) - { - Log.Error("Unable to detect the remote git service."); - return false; - } + var repository = new Lazy(() => new Repository(repositoryDirectory)); + try + { + string commitId = options.CommitId ?? repository.Value.Head.Commits.FirstOrDefault()?.Sha; + if (commitId == null) + { + Log.Error("No commit is checked out to HEAD. Have you committed yet?"); + return false; + } - try - { - Repository repo = repository.Value; - repoSourceFiles = sourceFiles.ToDictionary(e => e, e => repo.GetNormalizedPath(e)); - } - catch (RepositoryNotFoundException) - { - // Normalize using file system since we can't find the git repo. - Log.Warning($"Unable to find git repo at \"{options.GitWorkingDirectory}\". Using file system to find canonical capitalization of file paths."); - repoSourceFiles = sourceFiles.ToDictionary(e => e, e => GetNormalizedPath(e, workingDirectory)); - } + var providerManager = new Providers.ProviderManager(); + Providers.IProvider provider; + if (options.GitRemoteUrl == null) + { + var candidateProviders = from remote in repository.Value.Network.Remotes + let p = providerManager.GetProvider(remote.Url) + where p != null + select p; + provider = candidateProviders.FirstOrDefault(); + } + else + { + provider = providerManager.GetProvider(options.GitRemoteUrl.AbsoluteUri); + } - string rawUrl = provider.RawGitUrl; - if (rawUrl.Contains(RevisionPlaceholder) || rawUrl.Contains(FilenamePlaceholder)) - { - if (!rawUrl.Contains(RevisionPlaceholder) || !rawUrl.Contains(FilenamePlaceholder)) - { - Log.Error("Supplied custom URL pattern must contain both a revision and a filename placeholder."); - return false; - } - - rawUrl = rawUrl - .Replace(RevisionPlaceholder, "{0}") - .Replace(FilenamePlaceholder, "%var2%"); - } - else - { - rawUrl = $"{rawUrl}/{{0}}/%var2%"; - } + if (provider == null) + { + Log.Error("Unable to detect the remote git service."); + return false; + } - Log.Info($"Using {string.Format(rawUrl, commitId)} for source server URLs."); - var srcSrvContext = new SrcSrvContext - { - RawUrl = rawUrl, - DownloadWithPowershell = options.Method == LinkMethod.Powershell, - Revision = commitId, - }; - foreach (var sourceFile in repoSourceFiles) - { - // Skip files that aren't tracked by source control. - if (sourceFile.Value != null) - { - string relativePathForUrl = ReplaceSlashes(provider, sourceFile.Value); - srcSrvContext.Paths.Add(Tuple.Create(sourceFile.Key, relativePathForUrl)); - } - } + try + { + Repository repo = repository.Value; + repoSourceFiles = sourceFiles.ToDictionary(e => e, e => repo.GetNormalizedPath(e)); + } + catch (RepositoryNotFoundException) + { + // Normalize using file system since we can't find the git repo. + Log.Warning($"Unable to find git repo at \"{options.GitWorkingDirectory}\". Using file system to find canonical capitalization of file paths."); + repoSourceFiles = sourceFiles.ToDictionary(e => e, e => GetNormalizedPath(e, workingDirectory)); + } - // When using the VisualStudioTeamServicesProvider, add extra infomration to dictionary with VSTS-specific data - if (provider is Providers.VisualStudioTeamServicesProvider) + string rawUrl = provider.RawGitUrl; + if (rawUrl.Contains(RevisionPlaceholder) || rawUrl.Contains(FilenamePlaceholder)) + { + if (!rawUrl.Contains(RevisionPlaceholder) || !rawUrl.Contains(FilenamePlaceholder)) { - srcSrvContext.VstsData["TFS_COLLECTION"] = provider.CompanyUrl; - srcSrvContext.VstsData["TFS_TEAM_PROJECT"] = provider.ProjectName; - srcSrvContext.VstsData["TFS_REPO"] = provider.ProjectUrl; + Log.Error("Supplied custom URL pattern must contain both a revision and a filename placeholder."); + return false; } - CreateSrcSrv(projectSrcSrvFile, srcSrvContext); + rawUrl = rawUrl + .Replace(RevisionPlaceholder, "{0}") + .Replace(FilenamePlaceholder, "%var2%"); } - catch (RepositoryNotFoundException) + else { - Log.Error($"Unable to find git repo at \"{options.GitWorkingDirectory}\"."); - return false; + rawUrl = $"{rawUrl}/{{0}}/%var2%"; } - finally + + Log.Info($"Using {string.Format(rawUrl, commitId)} for source server URLs."); + var srcSrvContext = new SrcSrvContext { - if (repository.IsValueCreated) + RawUrl = rawUrl, + DownloadWithPowershell = options.Method == LinkMethod.Powershell, + Revision = commitId, + }; + foreach (var sourceFile in repoSourceFiles) + { + // Skip files that aren't tracked by source control. + if (sourceFile.Value != null) { - repository.Value.Dispose(); + string relativePathForUrl = ReplaceSlashes(provider, sourceFile.Value); + srcSrvContext.Paths.Add(Tuple.Create(sourceFile.Key, relativePathForUrl)); } } + + // When using the VisualStudioTeamServicesProvider, add extra infomration to dictionary with VSTS-specific data + if (provider is Providers.VisualStudioTeamServicesProvider) + { + srcSrvContext.VstsData["TFS_COLLECTION"] = provider.CompanyUrl; + srcSrvContext.VstsData["TFS_TEAM_PROJECT"] = provider.ProjectName; + srcSrvContext.VstsData["TFS_REPO"] = provider.ProjectUrl; + } + + CreateSrcSrv(projectSrcSrvFile, srcSrvContext); + } + catch (RepositoryNotFoundException) + { + Log.Error($"Unable to find git repo at \"{options.GitWorkingDirectory}\"."); + return false; + } + finally + { + if (repository.IsValueCreated) + { + repository.Value.Dispose(); + } } Log.Debug("Created source server link file, updating pdb file \"{0}\"", Catel.IO.Path.GetRelativePath(pdbPath, repositoryDirectory)); PdbStrHelper.Execute(PdbStrExePath, pdbPath, projectSrcSrvFile); var indexedFilesCount = repoSourceFiles.Values.Count(v => v != null); - Log.Info($"Remote git source information for {indexedFilesCount}/{sourceFiles.Count()} files written to pdb: \"{pdbPath}\""); + Log.Info($"Remote git source information for {indexedFilesCount}/{sourceFiles.Count} files written to pdb: \"{pdbPath}\""); return true; } - private static IEnumerable GetSourceFilesFromDepot(string repositoryDirectory) + private static List GetSourceFilesFromPdb(string pdbPath) + { + using (var pdb = new PdbFile(pdbPath)) + { + return pdb.GetFilesAndChecksums().Keys.ToList(); + } + } + + private static List GetSourceFilesFromDepot(string repositoryDirectory) { IEnumerable sourceFiles; var repo = new Repository(repositoryDirectory); @@ -217,7 +226,7 @@ where string.Equals(ext, ".cs", StringComparison.OrdinalIgnoreCase) select file; } - return sourceFiles; + return sourceFiles.ToList(); } private static void CreateSrcSrv(string srcsrvFile, SrcSrvContext srcSrvContext) From b3c896a43060e0339fcfdf4ab8014b01cccbe0dc Mon Sep 17 00:00:00 2001 From: Julien Richard Date: Mon, 31 Jul 2017 18:52:34 +0200 Subject: [PATCH 09/13] Option SkipVerify is back --- src/GitLink/Linker.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index e9541b9..acaeda5 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -66,7 +66,7 @@ public static class Linker } else { - sourceFiles = GetSourceFilesFromPdb(pdbPath); + sourceFiles = GetSourceFilesFromPdb(pdbPath, !options.SkipVerify); string someSourceFile = sourceFiles.FirstOrDefault(); if (someSourceFile == null) @@ -197,11 +197,24 @@ public static class Linker return true; } - private static List GetSourceFilesFromPdb(string pdbPath) + private static List GetSourceFilesFromPdb(string pdbPath, bool verifyFiles) { using (var pdb = new PdbFile(pdbPath)) { - return pdb.GetFilesAndChecksums().Keys.ToList(); + var sources = pdb.GetFilesAndChecksums().Keys.ToList(); + + if (verifyFiles) + { + Log.Debug("Verifying pdb files"); + + var missingFiles = pdb.FindMissingOrChangedSourceFiles(); + foreach (var missingFile in missingFiles) + { + Log.Warning($"File \"{missingFile}\" missing or changed since the PDB was compiled."); + } + } + + return sources; } } From 09717ef13de16bedaf34b10e821ba086b97fccc4 Mon Sep 17 00:00:00 2001 From: Shai Nahum Date: Thu, 24 Aug 2017 11:50:29 +0300 Subject: [PATCH 10/13] Adding support for TFS collections on VSTS (VisualStudio.com) --- CONTRIBUTORS | 1 + .../VisualStudioTeamServicesProviderFacts.cs | 57 +++++++------------ .../VisualStudioTeamServicesProvider.cs | 19 ++++++- 3 files changed, 41 insertions(+), 36 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 4de36da..096cee2 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -14,3 +14,4 @@ Andrew Arnott Geert van Horrik Wesley Eledui Marek Fišera +Shai Nahum diff --git a/src/GitLink.Tests/Providers/VisualStudioTeamServicesProviderFacts.cs b/src/GitLink.Tests/Providers/VisualStudioTeamServicesProviderFacts.cs index eeaf596..86b782d 100644 --- a/src/GitLink.Tests/Providers/VisualStudioTeamServicesProviderFacts.cs +++ b/src/GitLink.Tests/Providers/VisualStudioTeamServicesProviderFacts.cs @@ -35,60 +35,47 @@ public void ReturnsInValidInitialization() [TestFixture] public class TheVisualStudioTeamServicesProviderProperties { - [TestCase] - public void ReturnsValidCompany() + [TestCase("https://CatenaLogic.visualstudio.com/_git/main-repo", "main-repo")] + [TestCase("https://CatenaLogic.visualstudio.com/BigProject/_git/main-repo", "BigProject")] + [TestCase("https://CatenaLogic.visualstudio.com/DefaultCollection/BigProject/_git/main-repo", "BigProject")] + public void ReturnsValidProject(string url, string expectedProjectName) { var provider = new VisualStudioTeamServicesProvider(); - provider.Initialize("https://CatenaLogic.visualstudio.com/_git/main-repo"); + provider.Initialize(url); - Assert.AreEqual("CatenaLogic", provider.CompanyName); + Assert.AreEqual(expectedProjectName, provider.ProjectName); } - [TestCase] - public void ReturnsValidCompanyUrl() + [TestCase("https://CatenaLogic.visualstudio.com/_git/main-repo", "CatenaLogic")] + public void ReturnsValidCompany(string url, string expectedCompanyName) { var provider = new VisualStudioTeamServicesProvider(); - provider.Initialize("https://CatenaLogic.visualstudio.com/_git/main-repo"); + provider.Initialize(url); - Assert.AreEqual("https://CatenaLogic.visualstudio.com/", provider.CompanyUrl); + Assert.AreEqual(expectedCompanyName, provider.CompanyName); } - [TestCase] - public void ReturnsValidProject() + [TestCase("https://CatenaLogic.visualstudio.com/Project/_git/main-repo", "main-repo")] + [TestCase("https://CatenaLogic.visualstudio.com/Project/_git/main.repo", "main.repo")] + [TestCase("https://CatenaLogic.visualstudio.com/DefaultCollection/Project/_git/main.repo", "main.repo")] + public void ReturnsValidRepositoryName(string url, string expectedProjectUrl) { var provider = new VisualStudioTeamServicesProvider(); - provider.Initialize("https://CatenaLogic.visualstudio.com/_git/main-repo"); + provider.Initialize(url); - Assert.AreEqual("main-repo", provider.ProjectName); + Assert.AreEqual(expectedProjectUrl, provider.ProjectUrl); } - [TestCase] - public void ReturnsValidProject2() + [TestCase("https://CatenaLogic.visualstudio.com/_git/main-repo", "https://CatenaLogic.visualstudio.com/")] + [TestCase("https://CatenaLogic.visualstudio.com/DefaultCollection/BigProject/_git/main-repo", "https://CatenaLogic.visualstudio.com/DefaultCollection/")] + [TestCase("https://CatenaLogic.visualstudio.com/Other.Collection/BigProject/_git/main-repo", "https://CatenaLogic.visualstudio.com/Other.Collection/")] + public void ReturnsValidCompanyUrl(string url, string expectedCompanyUrl) { var provider = new VisualStudioTeamServicesProvider(); - provider.Initialize("https://CatenaLogic.visualstudio.com/BigProject/_git/main-repo"); + provider.Initialize(url); - Assert.AreEqual("BigProject", provider.ProjectName); + Assert.AreEqual(expectedCompanyUrl, provider.CompanyUrl); } - - [TestCase] - public void ReturnsValidRepositoryName() - { - var provider = new VisualStudioTeamServicesProvider(); - provider.Initialize("https://CatenaLogic.visualstudio.com/Project/_git/main-repo"); - - Assert.AreEqual("main-repo", provider.ProjectUrl); - } - - [TestCase] - public void ReturnsValidRepositoryNameWhenContainsPeriod() - { - var provider = new VisualStudioTeamServicesProvider(); - provider.Initialize("https://CatenaLogic.visualstudio.com/Big.Project/_git/main.repo"); - - Assert.AreEqual("main.repo", provider.ProjectUrl); - } - } } } diff --git a/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs b/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs index f891079..f33e36d 100644 --- a/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs +++ b/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs @@ -13,7 +13,24 @@ namespace GitLink.Providers public class VisualStudioTeamServicesProvider : ProviderBase { - private static readonly Regex HostingUrlPattern = new Regex(@"(?(?(?:https://)?(?([a-zA-Z0-9\-\.]*)?)\.visualstudio\.com/)(?[a-zA-Z0-9\-\.]*)/?_git//?(?[^/]+))"); + // Matches the git origin URL, providing named capture groups + // Example match: https://user.visualstudio.com/DefaultCollection/MyFirstProject/_git/MyFirstRepo + private static readonly Regex HostingUrlPattern = + new Regex( + @"(? + (?:https://)? + (?([a-zA-Z0-9\-\.]*)?) # account name (e.g. user) + \.visualstudio\.com/ + ( + [a-zA-Z0-9\-\.]+/ # collection (optional). e.g. DefaultCollection/ + (?!/?_git/) # Negative lookahead to avoid capturing 'project' group + )? + ) + (?[a-zA-Z0-9\-\.]*) # project name. e.g. MyFirstProject + (?/?_git//?) + (?[^/]+) # the repository's name. e.g. MyFirstRepo + ", + RegexOptions.IgnorePatternWhitespace); public VisualStudioTeamServicesProvider() : base(new GitPreparer()) From af67da0d2db2bfdff955bbe6b97a415e4c81553b Mon Sep 17 00:00:00 2001 From: Shai Nahum Date: Mon, 28 Aug 2017 14:40:20 +0300 Subject: [PATCH 11/13] (no change. triggering an AppVeyor rebuild) --- .../Providers/VisualStudioTeamServicesProviderFacts.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GitLink.Tests/Providers/VisualStudioTeamServicesProviderFacts.cs b/src/GitLink.Tests/Providers/VisualStudioTeamServicesProviderFacts.cs index 86b782d..2b4837d 100644 --- a/src/GitLink.Tests/Providers/VisualStudioTeamServicesProviderFacts.cs +++ b/src/GitLink.Tests/Providers/VisualStudioTeamServicesProviderFacts.cs @@ -19,7 +19,7 @@ public void ReturnsValidInitialization() { var provider = new VisualStudioTeamServicesProvider(); var valid = provider.Initialize("https://my-account.visualstudio.com/_git/main-repo"); - + Assert.IsTrue(valid); } From 4934d4fdde1baebc8ac5241701d2c1f3a00ef5a1 Mon Sep 17 00:00:00 2001 From: Ethan Date: Thu, 31 Aug 2017 01:27:25 -0700 Subject: [PATCH 12/13] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 92492a9..c3ac774 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ GitLink ![GitLink](design/logo/logo_64.png) -GitLink let's users step through your code hosted on GitHub! **Help make .NET open source projects more accessible by enabling this for your .NET projects, it's just a single additional step in your build**. See the list of [projects using GitLink](#projects-using-gitlink). +GitLink lets users step through your code hosted on GitHub! **Help make .NET open source projects more accessible by enabling this for your .NET projects, it's just a single additional step in your build**. See the list of [projects using GitLink](#projects-using-gitlink). Click here to lend your support to: GitLink and make a donation at pledgie.com ! From d3dd63e4ab3905ab2eed8659b3b432d92be6bae4 Mon Sep 17 00:00:00 2001 From: Julien Richard Date: Sun, 10 Sep 2017 20:57:18 +0200 Subject: [PATCH 13/13] Properly detect when .pdb file use the new Portable PDB format --- src/GitLink/GitLink.csproj | 1 + src/GitLink/Helpers/PortablePdbHelper.cs | 55 ++++++++++++++++++++++++ src/GitLink/Linker.cs | 6 +++ 3 files changed, 62 insertions(+) create mode 100644 src/GitLink/Helpers/PortablePdbHelper.cs diff --git a/src/GitLink/GitLink.csproj b/src/GitLink/GitLink.csproj index f7d723b..7dee726 100644 --- a/src/GitLink/GitLink.csproj +++ b/src/GitLink/GitLink.csproj @@ -75,6 +75,7 @@ + diff --git a/src/GitLink/Helpers/PortablePdbHelper.cs b/src/GitLink/Helpers/PortablePdbHelper.cs new file mode 100644 index 0000000..df705a2 --- /dev/null +++ b/src/GitLink/Helpers/PortablePdbHelper.cs @@ -0,0 +1,55 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitLink +{ + using System.IO; + + internal static class PortablePdbHelper + { + /// + /// Is the given .pdb using the new Portable format ? (https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md) + /// + /// .pdb file path + /// Returns if it's a Portable PDB + public static bool IsPortablePdb(string pdbPath) + { + using (var fs = File.Open(pdbPath, FileMode.Open, FileAccess.Read)) + using (var br = new BinaryReader(fs)) + { + // More infos in chapter II.24.2 of ECMA-335 (http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf) + var signature = 0x424A5342; + if (br.ReadUInt32() != signature) + { + return false; + } + + var majorVersion = br.ReadUInt16(); + if (majorVersion != 1) + { + return false; + } + + var minorVersion = br.ReadUInt16(); + if (minorVersion != 1) + { + return false; + } + + var reserved = br.ReadUInt32(); + if (reserved != 0) + { + return false; + } + + var versionLength = br.ReadUInt32(); + var version = System.Text.Encoding.UTF8.GetString(br.ReadBytes((int)versionLength)); + + return version.StartsWith("PDB v1.0"); + } + } + } +} \ No newline at end of file diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index acaeda5..8a7159a 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -50,6 +50,12 @@ public static class Linker } } + if (PortablePdbHelper.IsPortablePdb(pdbPath)) + { + Log.Warning("Portable PDB format is not compatible with GitLink. Please use SourceLink (https://github.com/ctaggart/SourceLink)."); + return true; + } + if (options.IndexAllDepotFiles) { if (repositoryDirectory == null)