Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Save PR merge base file as UTF-8 if working file is UTF-8 #1011

Merged
merged 1 commit into from
Jun 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/GitHub.App/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[assembly: SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Scope = "member", Target = "GitHub.Caches.CredentialCache.#InsertObject`1(System.String,!!0,System.Nullable`1<System.DateTimeOffset>)")]
[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "Git", Scope = "resource", Target = "GitHub.App.Resources.resources")]
[assembly: SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object,System.Object)", Scope = "member", Target = "GitHub.Services.PullRequestService.#CreateTempFile(System.String,System.String,System.String)")]
[assembly: SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object,System.Object)", Scope = "member", Target = "GitHub.Services.PullRequestService.#CreateTempFile(System.String,System.String,System.String,System.Text.Encoding)")]
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
Expand Down
54 changes: 46 additions & 8 deletions src/GitHub.App/Services/PullRequestService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -299,15 +299,53 @@ public IObservable<Tuple<string, string>> ExtractDiffFiles(
throw new FileNotFoundException($"Couldn't find merge base between {baseSha} and {headSha}.");
}

// We've found the merge base so these should already be fetched.
var left = await ExtractToTempFile(repo, mergeBase, fileName);
var right = isPullRequestBranchCheckedOut ?
Path.Combine(repository.LocalPath, fileName) : await ExtractToTempFile(repo, headSha, fileName);
string left;
string right;
if (isPullRequestBranchCheckedOut)
{
right = Path.Combine(repository.LocalPath, fileName);
left = await ExtractToTempFile(repo, mergeBase, fileName, GetEncoding(right));
}
else
{
left = await ExtractToTempFile(repo, mergeBase, fileName, Encoding.Default);
right = await ExtractToTempFile(repo, headSha, fileName, Encoding.Default);
}

return Observable.Return(Tuple.Create(left, right));
});
}

static Encoding GetEncoding(string file)
{
if (File.Exists(file))
{
var encoding = Encoding.UTF8;
if (IsEncoding(file, encoding))
{
return encoding;
}
}

return Encoding.Default;
}

static bool IsEncoding(string file, Encoding encoding)
{
using (var stream = File.OpenRead(file))
{
foreach (var b in encoding.GetPreamble())
{
if(b != stream.ReadByte())
{
return false;
}
}
}

return true;
}

public IObservable<Unit> RemoveUnusedRemotes(ILocalRepositoryModel repository)
{
return Observable.Defer(async () =>
Expand Down Expand Up @@ -362,20 +400,20 @@ string CreateUniqueRemoteName(IRepository repo, string name)
return uniqueName;
}

async Task<string> ExtractToTempFile(IRepository repo, string commitSha, string fileName)
async Task<string> ExtractToTempFile(IRepository repo, string commitSha, string fileName, Encoding encoding)
{
var contents = await gitClient.ExtractFile(repo, commitSha, fileName) ?? string.Empty;
return CreateTempFile(fileName, commitSha, contents);
return CreateTempFile(fileName, commitSha, contents, encoding);
}

static string CreateTempFile(string fileName, string commitSha, string contents)
static string CreateTempFile(string fileName, string commitSha, string contents, Encoding encoding)
{
var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
var tempFileName = $"{Path.GetFileNameWithoutExtension(fileName)}@{commitSha}{Path.GetExtension(fileName)}";
var tempFile = Path.Combine(tempDir, tempFileName);

Directory.CreateDirectory(tempDir);
File.WriteAllText(tempFile, contents, Encoding.UTF8);
File.WriteAllText(tempFile, contents, encoding);
return tempFile;
}

Expand Down
27 changes: 27 additions & 0 deletions src/UnitTests/GitHub.App/Services/PullRequestServiceTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
Expand Down Expand Up @@ -128,6 +129,32 @@ public async Task CheckedOut_BaseFromWorkingFile()
Assert.Equal(workingFile, files.Item2);
}

// https://github.com/github/VisualStudio/issues/1010
[Theory]
[InlineData("utf-8")] // Unicode (UTF-8)
[InlineData("Windows-1252")] // Western European (Windows)
public async Task CheckedOut_DifferentEncodings(string encodingName)
{
var encoding = Encoding.GetEncoding(encodingName);
var repoDir = Path.GetTempPath();
var baseFileContent = "baseFileContent";
var headFileContent = null as string;
var fileName = "fileName.txt";
var baseSha = "baseSha";
var headSha = "headSha";
var baseRef = new GitReferenceModel("ref", "label", baseSha, "uri");
var checkedOut = true;
var workingFile = Path.Combine(repoDir, fileName);
var workingFileContent = baseFileContent;
File.WriteAllText(workingFile, workingFileContent, encoding);

var files = await ExtractDiffFiles(baseSha, baseFileContent, headSha, headFileContent,
baseSha, baseFileContent, fileName, checkedOut, repoDir);

Assert.Equal(File.ReadAllText(files.Item1), File.ReadAllText(files.Item2));
Assert.Equal(File.ReadAllBytes(files.Item1), File.ReadAllBytes(files.Item2));
}

[Fact]
public async Task HeadBranchNotAvailable_ThrowsFileNotFoundException()
{
Expand Down