-
Notifications
You must be signed in to change notification settings - Fork 662
Expand file tree
/
Copy pathGitHelper.cs
More file actions
133 lines (106 loc) · 5.62 KB
/
GitHelper.cs
File metadata and controls
133 lines (106 loc) · 5.62 KB
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
namespace GitVersion
{
using System;
using System.Linq;
using LibGit2Sharp;
public static class GitHelper
{
public static void NormalizeGitDirectory(string gitDirectory)
{
using (var repo = new Repository(gitDirectory))
{
var remote = EnsureOnlyOneRemoteIsDefined(repo);
Logger.WriteInfo(string.Format("Fetching from remote '{0}' using the following refspecs: {1}.",
remote.Name, string.Join(", ", remote.FetchRefSpecs.Select(r => r.Specification))));
var fetchOptions = BuildFetchOptions();
repo.Network.Fetch(remote, fetchOptions);
CreateMissingLocalBranchesFromRemoteTrackingOnes(repo, remote.Name);
if (!repo.Info.IsHeadDetached)
{
Logger.WriteInfo(string.Format("HEAD points at branch '{0}'.", repo.Refs.Head.TargetIdentifier));
return;
}
Logger.WriteInfo(string.Format("HEAD is detached and points at commit '{0}'.", repo.Refs.Head.TargetIdentifier));
CreateFakeBranchPointingAtThePullRequestTip(repo);
}
}
static FetchOptions BuildFetchOptions()
{
// TODO: Respect username/password of arguments?
var username = Environment.GetEnvironmentVariable("GITVERSION_REMOTE_USERNAME");
var password = Environment.GetEnvironmentVariable("GITVERSION_REMOTE_PASSWORD");
var fetchOptions = new FetchOptions();
if (!string.IsNullOrEmpty(username))
{
fetchOptions.Credentials = new Credentials { Username = username, Password = password };
}
return fetchOptions;
}
static void CreateFakeBranchPointingAtThePullRequestTip(Repository repo)
{
var remote = repo.Network.Remotes.Single();
var remoteTips = repo.Network.ListReferences(remote);
var headTipSha = repo.Head.Tip.Sha;
var refs = remoteTips.Where(r => r.TargetIdentifier == headTipSha).ToList();
if (refs.Count == 0)
{
var message = string.Format("Couldn't find any remote tips from remote '{0}' pointing at the commit '{1}'.", remote.Url, headTipSha);
throw new ErrorException(message);
}
if (refs.Count > 1)
{
var names = string.Join(", ", refs.Select(r => r.CanonicalName));
var message = string.Format("Found more than one remote tip from remote '{0}' pointing at the commit '{1}'. Unable to determine which one to use ({2}).", remote.Url, headTipSha, names);
throw new ErrorException(message);
}
var canonicalName = refs[0].CanonicalName;
Logger.WriteInfo(string.Format("Found remote tip '{0}' pointing at the commit '{1}'.", canonicalName, headTipSha));
if (!canonicalName.StartsWith("refs/pull/"))
{
var message = string.Format("Remote tip '{0}' from remote '{1}' doesn't look like a valid pull request.", canonicalName, remote.Url);
throw new ErrorException(message);
}
var fakeBranchName = canonicalName.Replace("refs/pull/", "refs/heads/pull/");
Logger.WriteInfo(string.Format("Creating fake local branch '{0}'.", fakeBranchName));
repo.Refs.Add(fakeBranchName, new ObjectId(headTipSha));
Logger.WriteInfo(string.Format("Checking local branch '{0}' out.", fakeBranchName));
repo.Checkout(fakeBranchName);
}
static void CreateMissingLocalBranchesFromRemoteTrackingOnes(Repository repo, string remoteName)
{
var prefix = string.Format("refs/remotes/{0}/", remoteName);
foreach (var remoteTrackingReference in repo.Refs.FromGlob(prefix + "*"))
{
var localCanonicalName = "refs/heads/" + remoteTrackingReference.CanonicalName.Substring(prefix.Length);
if (repo.Refs.Any(x => x.CanonicalName == localCanonicalName))
{
Logger.WriteInfo(string.Format("Skipping local branch creation since it already exists '{0}'.", remoteTrackingReference.CanonicalName));
continue;
}
Logger.WriteInfo(string.Format("Creating local branch from remote tracking '{0}'.", remoteTrackingReference.CanonicalName));
var symbolicReference = remoteTrackingReference as SymbolicReference;
if (symbolicReference == null)
{
repo.Refs.Add(localCanonicalName, new ObjectId(remoteTrackingReference.TargetIdentifier), true);
}
else
{
repo.Refs.Add(localCanonicalName, new ObjectId(symbolicReference.ResolveToDirectReference().TargetIdentifier), true);
}
}
}
static Remote EnsureOnlyOneRemoteIsDefined(IRepository repo)
{
var remotes = repo.Network.Remotes;
var howMany = remotes.Count();
if (howMany == 1)
{
var remote = remotes.Single();
Logger.WriteInfo(string.Format("One remote found ({0} -> '{1}').", remote.Name, remote.Url));
return remote;
}
var message = string.Format("{0} remote(s) have been detected. When being run on a TeamCity agent, the Git repository is expected to bear one (and no more than one) remote.", howMany);
throw new ErrorException(message);
}
}
}