From 19da72ed3fc849e75e8ec03f41af07bb4dcde331 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Fri, 19 Jun 2015 13:25:16 +1200 Subject: [PATCH 1/2] Resolve TODO on Checkout command Correctly cache changesets --- GitTfs/Commands/Checkout.cs | 14 +++++--- GitTfs/Core/GitRepository.cs | 60 +++++++++++++++++++++++------------ GitTfs/Core/IGitRepository.cs | 1 + 3 files changed, 51 insertions(+), 24 deletions(-) diff --git a/GitTfs/Commands/Checkout.cs b/GitTfs/Commands/Checkout.cs index 0ca4c7df0..c09ed1ed5 100644 --- a/GitTfs/Commands/Checkout.cs +++ b/GitTfs/Commands/Checkout.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.IO; +using System.Linq; using NDesk.Options; using Sep.Git.Tfs.Core; using StructureMap; @@ -43,10 +44,15 @@ public int Run(string id) long changesetId; if(!long.TryParse(id, out changesetId)) throw new GitTfsException("error: wrong format for changeset id..."); - //TODO - var sha = _globals.Repository.FindCommitHashByChangesetId(changesetId, "TODO"); - if (string.IsNullOrEmpty(sha)) - throw new GitTfsException("error: commit not found for this changeset id..."); + + var shas = _globals.Repository.FindCommitHashesByChangesetId(changesetId); + if (shas.Count == 0) + throw new GitTfsException("error: commit not found for " + changesetId.ToString() + " changeset id..."); + if (shas.Count > 1) + throw new GitTfsException("error: found more than one commit for " + changesetId.ToString() + " changeset id..."); + + var sha = shas.Single(); + if (ReturnShaOnly) { _stdout.Write(sha); diff --git a/GitTfs/Core/GitRepository.cs b/GitTfs/Core/GitRepository.cs index 90fd6ae96..99afc105c 100644 --- a/GitTfs/Core/GitRepository.cs +++ b/GitTfs/Core/GitRepository.cs @@ -44,10 +44,18 @@ public GitCommit Commit(LogEntry logEntry) logEntry.Tree, parents, false); - changesetsCache[logEntry.ChangesetId] = commit.Sha; + AddToChangesetCache(logEntry.ChangesetId, commit.Sha); return new GitCommit(commit); } + private void AddToChangesetCache(long changesetId, string sha) + { + IList shas; + if (!changesetsCache.TryGetValue(changesetId, out shas)) + changesetsCache[changesetId] = shas = new List(); + shas.Add(sha); + } + public void UpdateRef(string gitRefName, string shaCommit, string message = null) { if (message == null) @@ -341,7 +349,7 @@ private void FindTfsParentCommits(List changesets, Commit comm public TfsChangesetInfo GetTfsChangesetById(string remoteRef, long changesetId, string tfsPath) { - var commit = FindCommitByChangesetId(changesetId, tfsPath, remoteRef); + var commit = FindCommitsByChangesetId(changesetId, remoteRef).FirstOrDefault(c => c.Message.Contains(tfsPath)); if (commit == null) return null; return TryParseChangesetInfo(commit.Message, commit.Sha); @@ -520,18 +528,23 @@ public bool CreateBranch(string gitBranchName, string target) return reference != null; } - private readonly Dictionary changesetsCache = new Dictionary(); + private readonly Dictionary> changesetsCache = new Dictionary>(); private bool cacheIsFull = false; public string FindCommitHashByChangesetId(long changesetId, string tfsPath) { - var commit = FindCommitByChangesetId(changesetId, tfsPath); + var commit = FindCommitsByChangesetId(changesetId).FirstOrDefault(c => c.Message.Contains(tfsPath)); if (commit == null) return null; return commit.Sha; } + public ICollection FindCommitHashesByChangesetId(long changesetId) + { + return FindCommitsByChangesetId(changesetId).Select(x => x.Sha).ToList(); + } + private static readonly Regex tfsIdRegex = new Regex("^git-tfs-id: .*;C([0-9]+)\r?$", RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.RightToLeft); public static bool TryParseChangesetId(string commitMessage, out long changesetId) @@ -547,17 +560,18 @@ public static bool TryParseChangesetId(string commitMessage, out long changesetI return false; } - private Commit FindCommitByChangesetId(long changesetId, string tfsPath, string remoteRef = null) + private IEnumerable FindCommitsByChangesetId(long changesetId, string remoteRef = null) { - Trace.WriteLine("Looking for changeset " + changesetId + " in git repository..."); + Trace.WriteLine("Looking for changeset " + changesetId.ToString() + " in git repository..."); if (remoteRef == null) { - string sha; - if (changesetsCache.TryGetValue(changesetId, out sha)) - return _repository.Lookup(sha); + IList shas; + if (changesetsCache.TryGetValue(changesetId, out shas)) + return shas.Select(sha => _repository.Lookup(sha)); + if (cacheIsFull) - return null; + return Enumerable.Empty(); } var reachableFromRemoteBranches = new CommitFilter @@ -570,24 +584,30 @@ private Commit FindCommitByChangesetId(long changesetId, string tfsPath, string reachableFromRemoteBranches.Since = _repository.Branches.Where(p => p.IsRemote && p.CanonicalName.EndsWith(remoteRef)); var commitsFromRemoteBranches = _repository.Commits.QueryBy(reachableFromRemoteBranches); - Commit commit = null; + var commits = new List(); foreach (var c in commitsFromRemoteBranches) { long id; if (TryParseChangesetId(c.Message, out id)) { - changesetsCache[changesetId] = c.Sha; - if (id == changesetId && c.Message.Contains(tfsPath)) - { - commit = c; - break; - } + AddToChangesetCache(changesetId, c.Sha); + + if (id == changesetId) + commits.Add(c); } } - if (remoteRef == null && commit == null) + + if (remoteRef == null && commits.Count == 0) cacheIsFull = true; // repository fully scanned - Trace.WriteLine((commit == null) ? " => Commit not found!" : " => Commit found! hash: " + commit.Sha); - return commit; + + if (commits.Count == 0) + Trace.WriteLine(" => Commit not found!"); + else if (commits.Count == 1) + Trace.WriteLine(" => Commit found! hash: " + commits.First().Sha); + else + Trace.WriteLine(" => Multiple commits found! hashes: " + string.Join(", ", commits.Select(c => c.Sha))); + + return commits; } public void CreateTag(string name, string sha, string comment, string Owner, string emailOwner, System.DateTime creationDate) diff --git a/GitTfs/Core/IGitRepository.cs b/GitTfs/Core/IGitRepository.cs index a6438487a..4ddfca4e5 100644 --- a/GitTfs/Core/IGitRepository.cs +++ b/GitTfs/Core/IGitRepository.cs @@ -42,6 +42,7 @@ public interface IGitRepository : IGitHelpers bool CreateBranch(string gitBranchName, string target); Branch RenameBranch(string oldName, string newName); string FindCommitHashByChangesetId(long changesetId, string tfsPath); + ICollection FindCommitHashesByChangesetId(long changesetId); void CreateTag(string name, string sha, string comment, string Owner, string emailOwner, System.DateTime creationDate); void CreateNote(string sha, string content, string owner, string emailOwner, DateTime creationDate); void MoveRemote(string oldRemoteName, string newRemoteName); From a1cb1c2df382f8ac3ecb713e68d871d924373478 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Sat, 20 Jun 2015 18:44:58 +1200 Subject: [PATCH 2/2] Add FindCommitByChangesetIdAndPath method --- GitTfs/Core/GitRepository.cs | 60 ++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/GitTfs/Core/GitRepository.cs b/GitTfs/Core/GitRepository.cs index 99afc105c..dc62b9702 100644 --- a/GitTfs/Core/GitRepository.cs +++ b/GitTfs/Core/GitRepository.cs @@ -349,7 +349,7 @@ private void FindTfsParentCommits(List changesets, Commit comm public TfsChangesetInfo GetTfsChangesetById(string remoteRef, long changesetId, string tfsPath) { - var commit = FindCommitsByChangesetId(changesetId, remoteRef).FirstOrDefault(c => c.Message.Contains(tfsPath)); + var commit = FindCommitByChangesetIdAndPath(changesetId, tfsPath, remoteRef); if (commit == null) return null; return TryParseChangesetInfo(commit.Message, commit.Sha); @@ -533,7 +533,7 @@ public bool CreateBranch(string gitBranchName, string target) public string FindCommitHashByChangesetId(long changesetId, string tfsPath) { - var commit = FindCommitsByChangesetId(changesetId).FirstOrDefault(c => c.Message.Contains(tfsPath)); + var commit = FindCommitByChangesetIdAndPath(changesetId, tfsPath); if (commit == null) return null; @@ -560,6 +560,62 @@ public static bool TryParseChangesetId(string commitMessage, out long changesetI return false; } + private Commit FindCommitByChangesetIdAndPath(long changesetId, string tfsPath, string remoteRef = null) + { + Trace.WriteLine("Looking for changeset " + changesetId.ToString() + " in git repository..."); + + if (remoteRef == null) + { + IList shas; + if (changesetsCache.TryGetValue(changesetId, out shas)) + { + var commitFromCache = shas.Select(sha => _repository.Lookup(sha)).FirstOrDefault(c => c.Message.Contains(tfsPath)); + if (commitFromCache != null) + return commitFromCache; + } + + if (cacheIsFull) + return null; + } + + var reachableFromRemoteBranches = new CommitFilter + { + Since = _repository.Branches.Where(p => p.IsRemote), + SortBy = CommitSortStrategies.Time + }; + + if (remoteRef != null) + reachableFromRemoteBranches.Since = _repository.Branches.Where(p => p.IsRemote && p.CanonicalName.EndsWith(remoteRef)); + + var commitsFromRemoteBranches = _repository.Commits.QueryBy(reachableFromRemoteBranches); + + Commit commit = null; + foreach (var c in commitsFromRemoteBranches) + { + long id; + if (TryParseChangesetId(c.Message, out id)) + { + AddToChangesetCache(changesetId, c.Sha); + + if (id == changesetId && c.Message.Contains(tfsPath)) + { + commit = c; + break; + } + } + } + + if (remoteRef == null && commit == null) + cacheIsFull = true; // repository fully scanned + + if (commit == null) + Trace.WriteLine(" => Commit not found!"); + else + Trace.WriteLine(" => Commit found! hash: " + commit.Sha); + + return commit; + } + private IEnumerable FindCommitsByChangesetId(long changesetId, string remoteRef = null) { Trace.WriteLine("Looking for changeset " + changesetId.ToString() + " in git repository...");