Git and GitHub Integration in C# Apps with LibGit2Sharp

Introduction

In this article, we will explore the integration of Git and GitHub commands into C# applications using the LibGit2Sharp NuGet package. Prior knowledge of Git and GitHub basics is recommended for better comprehension. If you're unfamiliar with these concepts, I encourage you to check out my previous article on Git and GitHub for a solid foundation.

Understanding Git and GitHub

Git is a distributed version control system used for tracking changes in source code during software development. GitHub, on the other hand, is a popular platform for hosting Git repositories and collaborating on projects. Together, they form a powerful ecosystem for managing codebase efficiently.

Integrating Git and GitHub with C#

Now, let's explore how we can leverage C# to interact with Git and GitHub repositories programmatically. We'll achieve this integration using the LibGit2Sharp NuGet package, a powerful tool that provides a C# API for executing Git commands seamlessly.

Libgit sharp

Installing LibGit2Sharp

Begin by installing the LibGit2Sharp NuGet package in your C# project. You can do this via the NuGet Package Manager in Visual Studio or by using the .NET CLI with the command “dotnet add package LibGit2Sharp”.

Browse

  1. Initializing a Git repository: With LibGit2Sharp, you can initialize a new Git repository within your C# application. Use the Repository.Init() method to create a new repository or Repository.Clone() to clone an existing repository from a URL.
  2. Clone Repository: The below code is used to create a clone of the repository from a given repository URL to a given Local repository folder.
    public void CloneRepo(string repositoryUrl, string localRepoPath, string userName, string password)
    {
        try
        {
            var options = new CloneOptions
            {
                FetchOptions =
                {
                    CredentialsProvider = (_url, _user, _cred) => new UsernamePasswordCredentials
                    {
                        Username = userName,
                        Password = password
                    }
                }
            };
            Repository.Clone(repositoryUrl, localRepoPath, options);
        }
        catch (LibGit2SharpException)
        {
            throw;
        }
    }
    
  3. In the above code, “repository” is the git hub URL, which ends with the .git extension as follows.
    Code
  4. “localRepoPath” is the one which folder path you wanted to clone it.
  5. In Options, you need to pass the “username” of your git hub account, which may be an email ID
  6. And for “password” you need to generate a token from settings-> developer settings
    Settings
  7. Click on Generate new token from Fine-grained tokens, then fill the form as required, like Token Name and Expired date.
  8. After filling in the data, click on Generate token.
    Fine grained tokens
  9. Now, you can get a unique token as shown below.
    generate new token
  10. You can copy this token and paste it in place of “password”.
  11. Get status: Below code is used to get the status of the given local repository.
    public string GetStatus(string localRepoPath)
    {
        string status = string.Empty;
        try
        {
            using (var repository = new Repository(localRepoPath))
            {
                status = string.Join(Environment.NewLine, repository.RetrieveStatus(new StatusOptions())
                                               .Select(item => $"{item.State}:{item.FilePath}"));
            }
        }
        catch (LibGit2SharpException)
        {
            throw;
        }
        return status;
    }
    
  12. Note. Return status will be empty when no changes (file modification) are made in the local repository.
  13. Stage files in Git: The below code is used to stage all the files in the local repository.
    public void StageFiles(string localRepoPath)
    {
        try
        {
            var files = from file in Directory.EnumerateFiles(localRepoPath) select file;
    
            using (var repository = new Repository(localRepoPath))
            {
                foreach (var file in files)
                {
                    var fileName = Path.GetFileName(file);
    
                    // Stage the file
                    repository.Index.Add(fileName);
                    repository.Index.Write();
                }
            }
        }
        catch (LibGit2SharpException)
        {
            throw;
        }
    }
    
  14. Commit files in Git: The below code is used to commit all stage files in the local repository.
    public void CommitFiles(string localRepoPath, string userName, string userEmail, string userComments)
    {
        try
        {
            using (var repository = new Repository(localRepoPath))
            {
                Signature author = new Signature(userName, userEmail, DateTime.Now);
                Signature committer = author;
    
                // Commit to the repository
                Commit commit = repository.Commit(userComments, author, committer);
            }
        }
        catch (LibGit2SharpException)
        {
            throw;
        }
    }
    
  15. Push files in GitHub: The below code is used to push all committed files from the local repository to the Github Repository path.
    public void CommitFiles(string localRepoPath, string repoUrl, string branchName, string userName, string password)
    {
        try
        {
            using (var repo = new Repository(localRepoPath))
            {
                var remote = repo.Network.Remotes["origin"];
                if (remote != null)
                {
                    repo.Network.Remotes.Remove("origin");
                }
                repo.Network.Remotes.Add("origin", repoUrl);
                remote = repo.Network.Remotes["origin"];
                if (remote == null)
                {
                    return;
                }
                FetchOptions fetop = new FetchOptions
                {
                    CredentialsProvider = (url, usernameFromUrl, types) =>
                        new UsernamePasswordCredentials
                        {
                            Username = userName,
                            Password = password
                        }
                };
                var refSpecs = remote.FetchRefSpecs.Select(x => x.Specification);
                Commands.Fetch(repo, remote.Name, refSpecs, fetop, string.Empty);
                var localBranchName = string.IsNullOrEmpty(branchName) ? "master" : branchName;
                // Get the branch you want to push
                var localBranch = repo.Branches[localBranchName];
                if (localBranch == null)
                {
                    return;
                }
                repo.Branches.Update(localBranch,
                    b => b.Remote = remote.Name,
                    b => b.UpstreamBranch = localBranch.CanonicalName);
                // Create a new push options object
                var pushOptions = new PushOptions
                {
                    CredentialsProvider = (url, usernameFromUrl, types) =>
                        new UsernamePasswordCredentials
                        {
                            Username = userName,
                            Password = password
                        }
                };
                // Push the branch to the remote repository
                repo.Network.Push(localBranch, pushOptions);
            }
        }
        catch (LibGit2SharpException)
        {
            throw;
        }
    }
  16. Pull Data from GitHub to local repository folder: The below code can be used to pull data from the Git hub Repository to the local repository path.
  17. It also includes merging data and handling conflicts.
    private void Pull(string localRepoPath, string userName, string password)
    {
        try
        {
            var options = new PullOptions();
            options.FetchOptions = new FetchOptions();
            options.FetchOptions.CredentialsProvider = new CredentialsHandler(
                (url, usernameFromUrl, types) =>
                    new UsernamePasswordCredentials()
                    {
                        Username = userName,
                        Password = password
                    });
            options.MergeOptions = new MergeOptions();
            options.MergeOptions.FastForwardStrategy = FastForwardStrategy.Default;
            options.MergeOptions.OnCheckoutNotify = new CheckoutNotifyHandler(showconflict);
            options.MergeOptions.CheckoutNotifyFlags = CheckoutNotifyFlags.Conflict;
            using (var repo = new Repository(localRepoPath))
            {
                Signature signature = repo.Config.BuildSignature(DateTimeOffset.Now);
                var result = Commands.Pull(repo, signature, options);
                if (result.Status == MergeStatus.Conflicts)
                {
                    Console.WriteLine("Conflict detected");
                    return;
                }
                if (result.Status == MergeStatus.UpToDate)
                {
                    Console.WriteLine("upto date");
                    return;
                }
            }
            Console.WriteLine("Pull successful");
        }
        catch (LibGit2SharpException)
        {
            throw;
        }
    }
    private bool showconflict(string path, CheckoutNotifyFlags notifyFlags)
    {
        if (notifyFlags is CheckoutNotifyFlags.Conflict)
        {
            Console.WriteLine("Conflict found in file :" + path);
        }
        return true;
    }

Handling Exceptions and Errors

Ensure your C# application gracefully handles exceptions and errors that may arise during Git operations. LibGit2Sharp provides exception classes (LibGit2SharpException) for different scenarios, allowing you to implement an appropriate error-handling mechanism


Similar Articles