libgit2libgit2sharp

Skip rebase step on conflict with Libgit2sharp


I am performing a rebase operation with libgit2sharp and it works all nice, but I and would like to skip a rebase step when a conflict occurs (if the commit has a specific message)


rebaseResult = _repository.Rebase.Start(branch, GetBranch(requiredBaseBranch),  null, new Identity(_configuration.CommitUserName, _configuration.CommitEmailAddress), options);
if (rebaseResult.Status == RebaseStatus.Complete)
    return true;
else
{
    if (rebaseResult.Status == RebaseStatus.Conflicts)
    {
        var currentStep = _repository.Rebase.GetCurrentStepInfo();

        if (currentStep.Commit.Message.Trim().EndsWith("Update packages", StringComparison.OrdinalIgnoreCase))
        {
            //is it possible to somehow skip and continue?
        }
    }
}

Is it possible? I had a look and the API doesn't seem to support it directly, but perhaps there is some more convoluted way to achieve this...


Solution

  • I ended up writing a small command line based class that skips the rebase. Posting the full solution that worked for me, for future reference:

    public class PoorManRebaseMachine
    {
        private readonly Action<string, ConsoleColor> _logDelegate;
        private readonly string _localRepoPath;
    
        public PoorManRebaseMachine(string localRepoPath, Action<string, ConsoleColor> logDelegate)
        {
            _logDelegate = logDelegate;
            _localRepoPath = localRepoPath;
        }
    
        public bool TryPoorManRebase(string storyNumber)
        {
            var errorOutput = RunGitCommandProcess("rebase master");
          
            if (IsUpdatePackagesRebaseConflict(errorOutput, storyNumber))
            {
                try
                {
                    return TrySkippingUpdatePackagesCommitsRecursive(storyNumber, errorOutput);
                }
                catch ( ex)
                {
                    _logDelegate($" while attempting automated rebase: {ex}", ConsoleColor.Red);
                    return false;
                }
            }
    
            _logDelegate($"Will not attempt automated rebase due to a conflict not resolvable automatically: {errorOutput}", ConsoleColor.Red);
            return false;
    
        }
    
        private bool TrySkippingUpdatePackagesCommitsRecursive(string storyNumber, string lastError)
        {
            if (IsUpdatePackagesRebaseConflict(lastError, storyNumber))
            {
                _logDelegate($"{GetRebaseStepNumber(lastError)} - Skipping rebase of: {GetRebaseConflictInfo(lastError)}", ConsoleColor.Cyan);
    
                var errorOutput = RunGitCommandProcess("rebase --skip");
    
                if (errorOutput.Contains("Successfully rebased and updated"))
                {
                    _logDelegate($"Automated rebase done: {errorOutput}", ConsoleColor.Green);
                    return true;
                }
                _logDelegate($"{GetRebaseStepNumber(lastError)} - DONE, Starting {GetRebaseStepNumber(errorOutput)}", ConsoleColor.DarkCyan);
    
                return TrySkippingUpdatePackagesCommitsRecursive(storyNumber, errorOutput);
            }
    
            throw new InvalidOperation($"The rebase conflict {lastError} cannot be solved automatically");
        }
    
        private static bool IsUpdatePackagesRebaseConflict(string conflictMessage, string storyNumber) => System.Text.RegularExpressions.Regex.IsMatch(conflictMessage, $"Could not apply .+... {storyNumber}[ -]*Update packages");
    
        private static string GetRebaseStepNumber(string conflictMessage) => conflictMessage.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
    
        private static string GetRebaseConflictInfo(string conflictMessage)
        {
            var line = conflictMessage.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries).LastOrDefault();
            return line?.Replace("Could not apply ", "");
        }
    
        private string RunGitCommandProcess(string commandName)
        {
            var p = new Process();
            var si = new ProcessStartInfo("git", commandName)
            {
                WorkingDirectory = _localRepoPath,
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true
            };
            p.StartInfo = si;
            p.Start();
            _ = p.StandardOutput.ReadToEnd(); //need to read output, as without it sometimes the process hangs on reading error
            var errorOutput = p.StandardError.ReadToEnd();
            p.WaitForExit();
            return errorOutput;
        }
    }