forked from GitTools/GitVersion
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLibGitExtensions.cs
241 lines (206 loc) · 9.23 KB
/
LibGitExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
namespace GitVersion
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using JetBrains.Annotations;
using LibGit2Sharp;
static class LibGitExtensions
{
public static DateTimeOffset When(this Commit commit)
{
return commit.Committer.When;
}
public static IEnumerable<SemanticVersion> GetVersionTagsOnBranch(this Branch branch, IRepository repository, string tagPrefixRegex)
{
var tags = repository.Tags.Select(t => t).ToList();
return repository.Commits.QueryBy(new CommitFilter
{
IncludeReachableFrom = branch.Tip
})
.SelectMany(c => tags.Where(t => c.Sha == t.Target.Sha).SelectMany(t =>
{
SemanticVersion semver;
if (SemanticVersion.TryParse(t.FriendlyName, tagPrefixRegex, out semver))
return new [] { semver };
return new SemanticVersion[0];
}));
}
public static Commit FindCommitBranchWasBranchedFrom([NotNull] this Branch branch, IRepository repository, params Branch[] excludedBranches)
{
const string missingTipFormat = "{0} has no tip. Please see http://example.com/docs for information on how to fix this.";
if (branch == null)
{
throw new ArgumentNullException("branch");
}
using (Logger.IndentLog("Finding branch source"))
{
if (branch.Tip == null)
{
Logger.WriteWarning(string.Format(missingTipFormat, branch.FriendlyName));
return null;
}
var otherBranches = repository.Branches
.Except(excludedBranches)
.Where(b => IsSameBranch(branch, b))
.ToList();
var mergeBases = otherBranches.Select(otherBranch =>
{
if (otherBranch.Tip == null)
{
Logger.WriteWarning(string.Format(missingTipFormat, otherBranch.FriendlyName));
return null;
}
// Otherbranch tip is a forward merge
var commitToFindCommonBase = otherBranch.Tip;
if (otherBranch.Tip.Parents.Contains(branch.Tip))
{
commitToFindCommonBase = otherBranch.Tip.Parents.First();
}
var findMergeBase = repository.ObjectDatabase.FindMergeBase(branch.Tip, commitToFindCommonBase);
if (findMergeBase != null)
{
using (Logger.IndentLog(string.Format("Found merge base of {0} against {1}", findMergeBase.Sha, otherBranch.FriendlyName)))
{
// We do not want to include merge base commits which got forward merged into the other branch
bool mergeBaseWasFowardMerge;
do
{
// Now make sure that the merge base is not a forward merge
mergeBaseWasFowardMerge = otherBranch.Commits
.SkipWhile(c => c != commitToFindCommonBase)
.TakeWhile(c => c != findMergeBase)
.Any(c => c.Parents.Contains(findMergeBase));
if (mergeBaseWasFowardMerge)
{
Logger.WriteInfo("Merge base was due to a forward merge, moving to next merge base");
var second = commitToFindCommonBase.Parents.First();
var mergeBase = repository.ObjectDatabase.FindMergeBase(branch.Tip, second);
if (mergeBase == findMergeBase) break;
findMergeBase = mergeBase;
}
} while (mergeBaseWasFowardMerge);
}
}
return new
{
mergeBaseCommit = findMergeBase,
branch = otherBranch
};
}).Where(b => b != null && b.mergeBaseCommit != null).OrderByDescending(b => b.mergeBaseCommit.Committer.When).ToList();
var firstOrDefault = mergeBases.FirstOrDefault();
if (firstOrDefault != null)
{
return firstOrDefault.mergeBaseCommit;
}
return null;
}
}
static bool IsSameBranch(Branch branch, Branch b)
{
return (b.IsRemote ?
b.FriendlyName.Substring(b.FriendlyName.IndexOf("/", StringComparison.Ordinal) + 1) :
b.FriendlyName) != branch.FriendlyName;
}
public static IEnumerable<Branch> GetBranchesContainingCommit([NotNull] this Commit commit, IRepository repository, IList<Branch> branches, bool onlyTrackedBranches)
{
if (commit == null)
{
throw new ArgumentNullException("commit");
}
var directBranchHasBeenFound = false;
foreach (var branch in branches)
{
if (branch.Tip != null && branch.Tip.Sha != commit.Sha || (onlyTrackedBranches && !branch.IsTracking))
{
continue;
}
directBranchHasBeenFound = true;
yield return branch;
}
if (directBranchHasBeenFound)
{
yield break;
}
foreach (var branch in branches.Where(b => (onlyTrackedBranches && !b.IsTracking)))
{
var commits = repository.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = branch }).Where(c => c.Sha == commit.Sha);
if (!commits.Any())
{
continue;
}
yield return branch;
}
}
private static Dictionary<string, GitObject> _cachedPeeledTarget = new Dictionary<string, GitObject>();
public static GitObject PeeledTarget(this Tag tag)
{
GitObject cachedTarget;
if(_cachedPeeledTarget.TryGetValue(tag.Target.Sha, out cachedTarget))
{
return cachedTarget;
}
var target = tag.Target;
while (target is TagAnnotation)
{
target = ((TagAnnotation)(target)).Target;
}
_cachedPeeledTarget.Add(tag.Target.Sha, target);
return target;
}
public static IEnumerable<Commit> CommitsPriorToThan(this Branch branch, DateTimeOffset olderThan)
{
return branch.Commits.SkipWhile(c => c.When() > olderThan);
}
public static bool IsDetachedHead(this Branch branch)
{
return branch.CanonicalName.Equals("(no branch)", StringComparison.OrdinalIgnoreCase);
}
public static string GetRepositoryDirectory(this IRepository repository, bool omitGitPostFix = true)
{
var gitDirectory = repository.Info.Path;
gitDirectory = gitDirectory.TrimEnd(Path.DirectorySeparatorChar);
if (omitGitPostFix && gitDirectory.EndsWith(".git"))
{
gitDirectory = gitDirectory.Substring(0, gitDirectory.Length - ".git".Length);
gitDirectory = gitDirectory.TrimEnd(Path.DirectorySeparatorChar);
}
return gitDirectory;
}
public static void CheckoutFilesIfExist(this IRepository repository, params string[] fileNames)
{
if (fileNames == null || fileNames.Length == 0)
{
return;
}
Logger.WriteInfo("Checking out files that might be needed later in dynamic repository");
foreach (var fileName in fileNames)
{
try
{
Logger.WriteInfo(string.Format(" Trying to check out '{0}'", fileName));
var headBranch = repository.Head;
var tip = headBranch.Tip;
var treeEntry = tip[fileName];
if (treeEntry == null)
{
continue;
}
var fullPath = Path.Combine(repository.GetRepositoryDirectory(), fileName);
using (var stream = ((Blob) treeEntry.Target).GetContentStream())
{
using (var streamReader = new BinaryReader(stream))
{
File.WriteAllBytes(fullPath, streamReader.ReadBytes((int)stream.Length));
}
}
}
catch (Exception ex)
{
Logger.WriteWarning(string.Format(" An error occurred while checking out '{0}': '{1}'", fileName, ex.Message));
}
}
}
}
}