Skip to content

Commit

Permalink
Fix unexpected behavior when accessing a branch that was added or del…
Browse files Browse the repository at this point in the history
…eted since the last update.
  • Loading branch information
BenLubar committed Mar 6, 2017
1 parent e40739d commit 37e9cbc
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 69 deletions.
45 changes: 34 additions & 11 deletions Git/Common/Clients/CommandLine/GitCommandLineClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,18 @@ public override async Task<bool> IsRepositoryValidAsync()

public override async Task CloneAsync(GitCloneOptions options)
{
var args = new GitArgumentsBuilder();
var args = new GitArgumentsBuilder("clone");

if (options.Branch != null)
{
args.Append("-b");
args.AppendQuoted(options.Branch);
}
if (options.RecurseSubmodules)
{
args.Append("--recursive");
}

args.Append("clone");
args.AppendSensitive(this.repository.GetRemoteUrlWithCredentials());
args.AppendQuoted(this.repository.LocalRepositoryPath);

Expand All @@ -59,20 +68,26 @@ public override async Task CloneAsync(GitCloneOptions options)
public override async Task UpdateAsync(GitUpdateOptions options)
{
/*
* git fetch origin <branch> | Get all changesets for the specified branch but does not apply them
* git reset --hard <ref> | Resets to the HEAD revision and removes commits that haven't been pushed
* git clean -dfq | Remove all non-Git versioned files and directories from the repository directory
* git remote set-url origin <url> | Make sure we're talking to the correct remote repository
* git fetch origin | Update the local cache of the remote repository
* git reset --hard <ref> | Resets to the HEAD revision and removes commits that haven't been pushed
* git clean -dfq | Remove all non-Git versioned files and directories from the repository working directory
* git submodule update --init --recursive | Updates submodules to the version referenced by the HEAD revision
*/

var args = new GitArgumentsBuilder("fetch");
args.AppendSensitive(this.repository.GetRemoteUrlWithCredentials());
args.Append("--quiet");
args.Append(options.Branch);
var remoteArgs = new GitArgumentsBuilder("remote set-url origin");
remoteArgs.AppendSensitive(this.repository.GetRemoteUrlWithCredentials());
await this.ExecuteCommandLineAsync(remoteArgs, this.repository.LocalRepositoryPath).ConfigureAwait(false);

await this.ExecuteCommandLineAsync(args, this.repository.LocalRepositoryPath).ConfigureAwait(false);
await this.ExecuteCommandLineAsync(new GitArgumentsBuilder("fetch origin"), this.repository.LocalRepositoryPath).ConfigureAwait(false);

var resetArgs = new GitArgumentsBuilder("reset --hard");
resetArgs.Append(AH.CoalesceString(options.Tag, "FETCH_HEAD"));
if (options.Tag != null)
resetArgs.AppendQuoted("origin/" + options.Tag);
else if (options.Branch != null)
resetArgs.AppendQuoted("origin/" + options.Branch);
else
resetArgs.Append("FETCH_HEAD");

await this.ExecuteCommandLineAsync(
resetArgs,
Expand All @@ -83,6 +98,14 @@ public override async Task UpdateAsync(GitUpdateOptions options)
new GitArgumentsBuilder("clean -dfq"),
this.repository.LocalRepositoryPath
).ConfigureAwait(false);

if (options.RecurseSubmodules)
{
await this.ExecuteCommandLineAsync(
new GitArgumentsBuilder("submodule update --init --recursive"),
this.repository.LocalRepositoryPath
).ConfigureAwait(false);
}
}

public override async Task<IEnumerable<string>> EnumerateRemoteBranchesAsync()
Expand Down
80 changes: 22 additions & 58 deletions Git/Common/Clients/LibGitSharp/LibGitSharpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,27 +49,14 @@ public override Task<IEnumerable<string>> EnumerateRemoteBranchesAsync()
throw new InvalidOperationException("Specified local repository path is invalid.");
}

try
{
this.log.LogDebug($"Creating temporary repository at '{this.repository.LocalRepositoryPath}'...");
Repository.Init(this.repository.LocalRepositoryPath);
using (var tempRepo = new Repository(this.repository.LocalRepositoryPath))
{
var refs = tempRepo.Network.ListReferences(this.repository.GetRemoteUrlWithCredentials());
var refs = Repository.ListRemoteReferences(this.repository.RemoteRepositoryUrl, this.CredentialsHandler);

var trimmedRefs = from r in refs
where r.CanonicalName.StartsWith("refs/heads/")
let trimmed = r.CanonicalName.Substring("refs/heads/".Length)
select trimmed;
var trimmedRefs = from r in refs
where r.CanonicalName.StartsWith("refs/heads/")
let trimmed = r.CanonicalName.Substring("refs/heads/".Length)
select trimmed;

return Task.FromResult(trimmedRefs);
}
}
finally
{
this.log.LogDebug($"Deleting temporary repository at '{this.repository.LocalRepositoryPath}'...");
DirectoryEx.Delete(this.repository.LocalRepositoryPath);
}
return Task.FromResult(trimmedRefs);
}
else
{
Expand Down Expand Up @@ -120,19 +107,24 @@ public override Task UpdateAsync(GitUpdateOptions options)
this.log.LogDebug($"Using repository at '{this.repository.LocalRepositoryPath}'...");
using (var repository = new Repository(this.repository.LocalRepositoryPath))
{
if (!string.IsNullOrEmpty(options.Branch))
this.log.LogDebug("Fetching commits from origin...");
repository.Fetch("origin", new FetchOptions { CredentialsProvider = CredentialsHandler, Prune = true });
var refName = "FETCH_HEAD";
if (options.Branch != null)
refName = "origin/" + options.Branch;
else if (options.Tag != null)
refName = "origin/" + options.Tag;
this.log.LogDebug($"Resetting the index and working tree to {refName}...");
repository.Reset(ResetMode.Hard, refName);
repository.RemoveUntrackedFiles();

if (options.RecurseSubmodules)
{
var branch = this.GetOrCreateLocalBranch(repository, options.Branch);
if (branch != null)
repository.Checkout(branch);
else
this.log.LogError("Branch not found in repository.");
foreach (var submodule in repository.Submodules)
{
repository.Submodules.Update(submodule.Name, new SubmoduleUpdateOptions { CredentialsProvider = CredentialsHandler, Init = true });
}
}

this.log.LogDebug("Fetching commits from origin...");
repository.Fetch("origin", new FetchOptions { CredentialsProvider = CredentialsHandler });
this.log.LogDebug("Resetting the index and working tree to FETCH_HEAD...");
repository.Reset(ResetMode.Hard, "FETCH_HEAD");
}

return Complete;
Expand All @@ -150,34 +142,6 @@ public override Task ArchiveAsync(string targetDirectory)
return Complete;
}

private Branch GetOrCreateLocalBranch(Repository repo, string localBranchName)
{
this.log.LogDebug($"Finding local branch '{localBranchName}'...");
var existing = repo.Branches[localBranchName];
if (existing != null)
{
this.log.LogDebug($"Using local branch '{existing.CanonicalName}'...");
return existing;
}

string trackedBranchName = "origin/" + localBranchName;
this.log.LogDebug($"Local branch not found, finding tracked branch '{trackedBranchName}'...");

var trackedBranch = repo.Branches[trackedBranchName];
if (trackedBranch == null)
{
this.log.LogError("Tracked branch not found.");
return null;
}

var localBranch = repo.CreateBranch(localBranchName, trackedBranch.Tip);

this.log.LogDebug($"Updating local branch to track remote branch '{trackedBranch.CanonicalName}'...");
repo.Branches.Update(localBranch, b => b.TrackedBranch = trackedBranch.CanonicalName);

return localBranch;
}

private LibGit2Sharp.Credentials CredentialsHandler(string url, string usernameFromUrl, SupportedCredentialTypes types)
{
if (string.IsNullOrEmpty(this.repository.UserName))
Expand Down

0 comments on commit 37e9cbc

Please sign in to comment.