Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Git command line changes for WSL #3893

Merged
merged 4 commits into from
Sep 27, 2024
Merged

Conversation

ssparach
Copy link
Contributor

@ssparach ssparach commented Sep 23, 2024

Summary of the pull request

This PR contains the last set of changes to allow file explorer integration of a WSL git repository. It does this by using WSL command line to obtain all property information from git.

References and relevant issues

#3829

Detailed description of the pull request / Additional comments

This PR:

  • Uses git rev-parse --show-toplevel to validate a git repository instead of LibGit2Sharp.Repository()
  • Uses git -no-optional-locks-branch --show-current to retrieve branch name, detached state
  • Adds --branch to RetrieveStatusInformation to obtain branch sha, ahead by value, behind by value and upstream branch info
  • Uses rev-parse HEAD to find head commit
  • Removes CommitWrapper(LibGit2Sharp.Commit commit) constructor and removes FindLastCommitUsingLibGit2Sharp from CommitLogCache

Note: This PR removes LibGit2Sharp usage where applicable and other unused library using statements. However, it does not completely clean usage of LibGit2Sharp library as the enum definitions from this library are being relied on such as FIleStatus etc. A separate bug has been created to track the complete removal of LibGit2Sharp from the git extension project (i.e. Create custom enum definitions for FileStatus, SubmoduleStatus to remove LibGit2Sharp dependency from git extension project · Issue #3894 · microsoft/devhome (github.com)). This improvement is not blocking to this PR.

Validation steps performed

Build of msix package
Unit Testing
Validation of functionality in VM
image

PR checklist

  • Closes #xxx
  • Tests added/passed
  • Documentation updated

@ssparach ssparach changed the title git command line changes for WSL Git command line changes for WSL Sep 23, 2024
@ssparach ssparach marked this pull request as ready for review September 23, 2024 20:20
}

var output = validateGitRootRepo.Output;
if (output is null || output.Contains("fatal: not a git repository"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the string fatal: not a git repository be in different languages? Should this be localized?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, it does not seem like the git output needs to be translated. The output is parsed to throw the right exception and eventually the error displayed to the user in Dev Home is localized.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this use the exit code of the process? I don't know if git.exe sets the exit code though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got curious. This seems like a complicated question. https://github.com/git/git/blob/master/po/README.md#marking-strings-for-translation

That's how the linux flavors of git do translation - I don't know all the details, but it seems to be more runtime-oriented and uses gettext in contrast to how we provide translations in Windows land. There might be some other stuff they use on Windows, but at current, it doesn't appear to be active in most cases: git-for-windows/git#724

Copy link
Member

@DefaultRyan DefaultRyan Sep 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this use the exit code of the process? I don't know if git.exe sets the exit code though.

I agree that parsing the output for this specific message feels somewhat brittle. We can most likely rely on git returning a non-zero exit code for failure, but it doesn't return any specific error codes to tell us what the problem was. Can we check the exit code of the process and also return a failure if it is non-zero?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we check the exit code of the process and also return a failure if it is non-zero?

Incorporated in latest changes. Thanks!

}

var output = validateGitRootRepo.Output;
if (output is null || output.Contains("fatal: not a git repository"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ssparach ssparach added the Needs-Second Pull request that needs another approval label Sep 26, 2024
_log.Error("GitLocalRepositoryProviderFactory", "Failed to create GitLocalRepository", libGitEx);
return new GetLocalRepositoryResult(libGitEx, _stringResource.GetLocalized("RepositoryNotFound"), $"Message: {libGitEx.Message} and HRESULT: {libGitEx.HResult}");
_log.Error(ex, "GitLocalRepositoryProviderFactory: Failed to create GitLocalRepository");
return new GetLocalRepositoryResult(ex, _stringResource.GetLocalized("RepositoryNotFound"), $"Message: {ex.Message} and HRESULT: {ex.HResult}");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Maybe this needs to be changed? Do we still tell the user "Repo not found"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repository not found.


if (process.ExitCode != 0)
{
Log.Error("Execute Git process exited unsuccessfully with exit code {ExitCode}", process.ExitCode);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

Suggested change
Log.Error("Execute Git process exited unsuccessfully with exit code {ExitCode}", process.ExitCode);
Log.Error($"Execute Git process exited unsuccessfully with exit code {process.ExitCode}");

if (WslIntegrator.IsWSLRepo(rootFolder))
{
var normalizedLinuxPath = WslIntegrator.GetNormalizedLinuxPath(rootFolder);
if (output.TrimEnd('\n') != normalizedLinuxPath)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Should we get Env.NewLine or do we want to hard code \n here?

Copy link
Contributor

@dhoehna dhoehna Sep 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think Env.NewLine is correct because this is for WSL. WIndows and Linux have different line endings. I'm afraid that Env.NewLine is the windows definition of a line ending.

EDIT: Unless this only applies to file endings and not line endings.

public GitCommandRunnerResultInfo(ProviderOperationStatus status, string? output)
{
Status = status;
Output = output;
}

public GitCommandRunnerResultInfo(ProviderOperationStatus status, string? displayMessage, string? diagnosticText, Exception? ex, string? args)
public GitCommandRunnerResultInfo(ProviderOperationStatus status, string? output, string? displayMessage, string? diagnosticText, Exception? ex, string? args, int? processExitCode)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With so many nullable parameters can you split the constructor into multiple constructors so callers can more easily use this object.

I don't like calling constructors with multiple nullable parameters because the constructor does not tell me what combination of values ill throw an exception.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding more constructors instead of more nullable parameters.

if (process.ExitCode != 0)
{
Log.Error("Execute Git process exited unsuccessfully with exit code {ExitCode}", process.ExitCode);
return new GitCommandRunnerResultInfo(ProviderOperationStatus.Failure, output, "Execute Git process exited unsuccessfully", string.Empty, null, arguments, process.ExitCode);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: exited with a failure error code "Exit unsuccessfully" tells me that the process was unsuccessful in exiting.

Log.Error("Execute Git process exited unsuccessfully with exit code {ExitCode}", process.ExitCode);
return new GitCommandRunnerResultInfo(ProviderOperationStatus.Failure, output, "Execute Git process exited unsuccessfully", string.Empty, null, arguments, process.ExitCode);
}

return new GitCommandRunnerResultInfo(ProviderOperationStatus.Success, output);
}
else
{
Log.Error("Failed to start the Git process: process is null");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: remove process is null The definition of a null process is one that did not start.

}
catch (Exception ex)
{
_log.Error("GitLocalRepositoryProviderFactory", "Failed to create GitLocalRepository", ex);
_log.Error(ex, "GitLocalRepositoryProviderFactory: Failed to create GitLocalRepository");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: nameof(GitLocalRepository)

private Commit? _head;
private readonly ILogger _log = Log.ForContext("SourceContext", nameof(RepositoryWrapper));

private string? _head;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a newline.

public void ValidateGitRepositoryRootPath(string rootFolder)
{
var validateGitRootRepo = GitExecute.ExecuteGitCommand(_gitDetect.GitConfiguration.ReadInstallPath(), rootFolder, "rev-parse --show-toplevel");
var output = validateGitRootRepo.Output;
Copy link
Contributor

@dhoehna dhoehna Sep 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename output to a name that describes the string. Maybe GitRootRepositoryPath?

throw validateGitRootRepo.Ex ?? new ArgumentException($"Not a valid git repository root path: RootFolder: {rootFolder} Git output: {output}");
}

if (WslIntegrator.IsWSLRepo(rootFolder))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the validation code and error reposting is the same. Maybe pull the call to get a normalized path and the error string into their own if block.

Then the error path check and error reporting can be de-duped.

var result = GitExecute.ExecuteGitCommand(_gitDetect.GitConfiguration.ReadInstallPath(), _workingDirectory, "rev-parse HEAD");
if (result.Status != ProviderOperationStatus.Success)
{
throw result.Ex ?? new InvalidOperationException(result.ProcessExitCode?.ToString(CultureInfo.InvariantCulture) ?? "Unknown error while obtaining HEAD commit");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we provide any information to the user instead of "Unknown error while obtaining HEAD commit"? Maybe a call to action. Can they check logs somewhere else?

var result = GitExecute.ExecuteGitCommand(_gitDetect.GitConfiguration.ReadInstallPath(), _workingDirectory, "rev-parse HEAD");
if (result.Status != ProviderOperationStatus.Success)
{
throw result.Ex ?? new InvalidOperationException(result.ProcessExitCode?.ToString(CultureInfo.InvariantCulture) ?? "Unknown error while obtaining HEAD commit");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed twice a check if the extension is not null. I thought the exception is always not null if the status is not success.

string? head = result.Output?.Trim();
if (string.IsNullOrEmpty(head))
{
throw new InvalidOperationException("Git command output is null or the repository has no commits");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No way to distinguish between a null value and a repo with no commits?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A repository with no commits is a valid repository state.

string.Format(CultureInfo.CurrentCulture, _folderStatusDetached, _repo.Head.Tip.Sha[..7]) :
string.Format(CultureInfo.CurrentCulture, _folderStatusBranch, _repo.Head.FriendlyName);
if (_repo.Head.IsTracking)
branchName = repoStatus.IsHeadDetached ?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: rename repoStatus to repositoryStatus

@ssparach
Copy link
Contributor Author

Thanks a lot for reviewing, everyone! I went through the PR comments and did not find any blocking issues. To speed up the selfhost of this functionality, I will go ahead and wrap up this PR now. I will be sure to address the minor suggestions in a future PR.

@ssparach ssparach merged commit c0936f9 into main Sep 27, 2024
4 checks passed
@krschau krschau removed the Needs-Second Pull request that needs another approval label Oct 2, 2024
@krschau krschau modified the milestone: Dev Home v0.19 Oct 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

File Explorer version control integration should work with WSL repos/paths
5 participants