ruby_git 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.commitlintrc.yml +16 -0
 - data/.github/CODEOWNERS +4 -0
 - data/.github/workflows/continuous_integration.yml +87 -0
 - data/.github/workflows/enforce_conventional_commits.yml +21 -0
 - data/.github/workflows/experimental_ruby_builds.yml +65 -0
 - data/.gitignore +3 -0
 - data/.husky/commit-msg +1 -0
 - data/.markdownlint.yml +25 -0
 - data/.rubocop.yml +13 -15
 - data/.yardopts +6 -1
 - data/CHANGELOG.md +58 -0
 - data/CONTRIBUTING.md +5 -5
 - data/{LICENSE.md → LICENSE.txt} +1 -1
 - data/PLAN.md +67 -0
 - data/README.md +58 -6
 - data/RELEASING.md +5 -54
 - data/Rakefile +31 -38
 - data/RubyGit Class Diagram.svg +1 -0
 - data/bin/command-line-test +189 -0
 - data/bin/console +2 -0
 - data/bin/setup +13 -2
 - data/lib/ruby_git/command_line/options.rb +61 -0
 - data/lib/ruby_git/command_line/result.rb +155 -0
 - data/lib/ruby_git/command_line/runner.rb +296 -0
 - data/lib/ruby_git/command_line.rb +95 -0
 - data/lib/ruby_git/encoding_normalizer.rb +49 -0
 - data/lib/ruby_git/errors.rb +169 -0
 - data/lib/ruby_git/repository.rb +33 -0
 - data/lib/ruby_git/status/branch.rb +92 -0
 - data/lib/ruby_git/status/entry.rb +162 -0
 - data/lib/ruby_git/status/ignored_entry.rb +44 -0
 - data/lib/ruby_git/status/ordinary_entry.rb +207 -0
 - data/lib/ruby_git/status/parser.rb +203 -0
 - data/lib/ruby_git/status/renamed_entry.rb +257 -0
 - data/lib/ruby_git/status/report.rb +143 -0
 - data/lib/ruby_git/status/stash.rb +33 -0
 - data/lib/ruby_git/status/submodule_status.rb +85 -0
 - data/lib/ruby_git/status/unmerged_entry.rb +248 -0
 - data/lib/ruby_git/status/untracked_entry.rb +52 -0
 - data/lib/ruby_git/status.rb +33 -0
 - data/lib/ruby_git/version.rb +1 -1
 - data/lib/ruby_git/worktree.rb +175 -33
 - data/lib/ruby_git.rb +91 -28
 - data/package.json +11 -0
 - data/ruby_git.gemspec +33 -23
 - metadata +146 -50
 - data/.travis.yml +0 -26
 - data/CODEOWNERS +0 -3
 - data/MAINTAINERS.md +0 -8
 - data/lib/ruby_git/error.rb +0 -8
 - data/lib/ruby_git/file_helpers.rb +0 -42
 - data/lib/ruby_git/git_binary.rb +0 -106
 - /data/{ISSUE_TEMPLATE.md → .github/ISSUE_TEMPLATE.md} +0 -0
 - /data/{PULL_REQUEST_TEMPLATE.md → .github/PULL_REQUEST_TEMPLATE.md} +0 -0
 
| 
         @@ -0,0 +1,92 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RubyGit
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Status
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Represents git branch information
         
     | 
| 
      
 6 
     | 
    
         
            +
                #
         
     | 
| 
      
 7 
     | 
    
         
            +
                # @api public
         
     | 
| 
      
 8 
     | 
    
         
            +
                class Branch
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # @attribute [rw] name
         
     | 
| 
      
 10 
     | 
    
         
            +
                  #
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # The name of the current branch
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #   branch.name #=> 'main'
         
     | 
| 
      
 15 
     | 
    
         
            +
                  #
         
     | 
| 
      
 16 
     | 
    
         
            +
                  # @return [String, nil] branch name or nil if detached HEAD
         
     | 
| 
      
 17 
     | 
    
         
            +
                  #
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 19 
     | 
    
         
            +
                  attr_accessor :name
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  # @attribute [rw] oid
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # The object ID (hash) of the current commit
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #   branch.oid #=> 'abcdef1234567890'
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # @return [String] commit hash
         
     | 
| 
      
 29 
     | 
    
         
            +
                  #
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 31 
     | 
    
         
            +
                  attr_accessor :oid
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  # @attribute [rw] upstream
         
     | 
| 
      
 34 
     | 
    
         
            +
                  #
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # The name of the upstream branch
         
     | 
| 
      
 36 
     | 
    
         
            +
                  #
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 38 
     | 
    
         
            +
                  #   branch.upstream #=> 'origin/main'
         
     | 
| 
      
 39 
     | 
    
         
            +
                  #
         
     | 
| 
      
 40 
     | 
    
         
            +
                  # @return [String, nil] upstream branch name or nil if no upstream
         
     | 
| 
      
 41 
     | 
    
         
            +
                  #
         
     | 
| 
      
 42 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 43 
     | 
    
         
            +
                  attr_accessor :upstream
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  # @attribute [rw] ahead
         
     | 
| 
      
 46 
     | 
    
         
            +
                  #
         
     | 
| 
      
 47 
     | 
    
         
            +
                  # Number of commits ahead of upstream
         
     | 
| 
      
 48 
     | 
    
         
            +
                  #
         
     | 
| 
      
 49 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 50 
     | 
    
         
            +
                  #   branch.ahead #=> 2
         
     | 
| 
      
 51 
     | 
    
         
            +
                  #
         
     | 
| 
      
 52 
     | 
    
         
            +
                  # @return [Integer] number of commits ahead
         
     | 
| 
      
 53 
     | 
    
         
            +
                  #
         
     | 
| 
      
 54 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 55 
     | 
    
         
            +
                  attr_accessor :ahead
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  # @attribute [rw] behind
         
     | 
| 
      
 58 
     | 
    
         
            +
                  #
         
     | 
| 
      
 59 
     | 
    
         
            +
                  # Number of commits behind upstream
         
     | 
| 
      
 60 
     | 
    
         
            +
                  #
         
     | 
| 
      
 61 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 62 
     | 
    
         
            +
                  #   branch.behind #=> 3
         
     | 
| 
      
 63 
     | 
    
         
            +
                  #
         
     | 
| 
      
 64 
     | 
    
         
            +
                  # @return [Integer] number of commits behind
         
     | 
| 
      
 65 
     | 
    
         
            +
                  #
         
     | 
| 
      
 66 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 67 
     | 
    
         
            +
                  attr_accessor :behind
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  # Check if the branch has an upstream configured
         
     | 
| 
      
 70 
     | 
    
         
            +
                  #
         
     | 
| 
      
 71 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 72 
     | 
    
         
            +
                  #   branch.upstream? #=> true
         
     | 
| 
      
 73 
     | 
    
         
            +
                  #
         
     | 
| 
      
 74 
     | 
    
         
            +
                  # @return [Boolean] true if upstream is configured
         
     | 
| 
      
 75 
     | 
    
         
            +
                  #
         
     | 
| 
      
 76 
     | 
    
         
            +
                  def upstream?
         
     | 
| 
      
 77 
     | 
    
         
            +
                    !@upstream.nil?
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  # Check if HEAD is detached
         
     | 
| 
      
 81 
     | 
    
         
            +
                  #
         
     | 
| 
      
 82 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 83 
     | 
    
         
            +
                  #   branch.detached? #=> true
         
     | 
| 
      
 84 
     | 
    
         
            +
                  #
         
     | 
| 
      
 85 
     | 
    
         
            +
                  # @return [Boolean] true if HEAD is detached
         
     | 
| 
      
 86 
     | 
    
         
            +
                  #
         
     | 
| 
      
 87 
     | 
    
         
            +
                  def detached?
         
     | 
| 
      
 88 
     | 
    
         
            +
                    @name.nil?
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
                end
         
     | 
| 
      
 91 
     | 
    
         
            +
              end
         
     | 
| 
      
 92 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,162 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RubyGit
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Status
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Base class for git status entries
         
     | 
| 
      
 6 
     | 
    
         
            +
                #
         
     | 
| 
      
 7 
     | 
    
         
            +
                # @api public
         
     | 
| 
      
 8 
     | 
    
         
            +
                class Entry
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # Status code mapping to symbols
         
     | 
| 
      
 10 
     | 
    
         
            +
                  STATUS_CODES = {
         
     | 
| 
      
 11 
     | 
    
         
            +
                    '.': :unmodified,
         
     | 
| 
      
 12 
     | 
    
         
            +
                    M: :modified,
         
     | 
| 
      
 13 
     | 
    
         
            +
                    T: :type_changed,
         
     | 
| 
      
 14 
     | 
    
         
            +
                    A: :added,
         
     | 
| 
      
 15 
     | 
    
         
            +
                    D: :deleted,
         
     | 
| 
      
 16 
     | 
    
         
            +
                    R: :renamed,
         
     | 
| 
      
 17 
     | 
    
         
            +
                    C: :copied,
         
     | 
| 
      
 18 
     | 
    
         
            +
                    U: :updated_but_unmerged,
         
     | 
| 
      
 19 
     | 
    
         
            +
                    '?': :untracked,
         
     | 
| 
      
 20 
     | 
    
         
            +
                    '!': :ignored
         
     | 
| 
      
 21 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  # Rename operation mapping to symbols
         
     | 
| 
      
 24 
     | 
    
         
            +
                  RENAME_OPERATIONS = {
         
     | 
| 
      
 25 
     | 
    
         
            +
                    'R' => :rename
         
     | 
| 
      
 26 
     | 
    
         
            +
                    # git status doesn't actually try to detect copies
         
     | 
| 
      
 27 
     | 
    
         
            +
                    # 'C' => :copy
         
     | 
| 
      
 28 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  # @attribute [r] path
         
     | 
| 
      
 31 
     | 
    
         
            +
                  #
         
     | 
| 
      
 32 
     | 
    
         
            +
                  # The path of the file
         
     | 
| 
      
 33 
     | 
    
         
            +
                  #
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 35 
     | 
    
         
            +
                  #   entry.path #=> 'lib/example.rb'
         
     | 
| 
      
 36 
     | 
    
         
            +
                  #
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # @return [String] file path
         
     | 
| 
      
 38 
     | 
    
         
            +
                  #
         
     | 
| 
      
 39 
     | 
    
         
            +
                  attr_reader :path
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  # Initialize a new entry
         
     | 
| 
      
 42 
     | 
    
         
            +
                  #
         
     | 
| 
      
 43 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 44 
     | 
    
         
            +
                  #   Entry.new('lib/example.rb')
         
     | 
| 
      
 45 
     | 
    
         
            +
                  #
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # @param path [String] file path
         
     | 
| 
      
 47 
     | 
    
         
            +
                  #
         
     | 
| 
      
 48 
     | 
    
         
            +
                  def initialize(path)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    @path = path
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  # Convert a status code to a symbol
         
     | 
| 
      
 53 
     | 
    
         
            +
                  #
         
     | 
| 
      
 54 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 55 
     | 
    
         
            +
                  #   Entry.status_to_symbol('M') #=> :modified
         
     | 
| 
      
 56 
     | 
    
         
            +
                  #
         
     | 
| 
      
 57 
     | 
    
         
            +
                  # @param code [String] status code
         
     | 
| 
      
 58 
     | 
    
         
            +
                  # @return [Symbol] status as symbol
         
     | 
| 
      
 59 
     | 
    
         
            +
                  #
         
     | 
| 
      
 60 
     | 
    
         
            +
                  def self.status_to_symbol(code)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    STATUS_CODES[code.to_sym] || :unknown
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  # Convert a rename operation to a symbol
         
     | 
| 
      
 65 
     | 
    
         
            +
                  #
         
     | 
| 
      
 66 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 67 
     | 
    
         
            +
                  #   Entry.rename_operation_to_symbol('R') #=> :rename
         
     | 
| 
      
 68 
     | 
    
         
            +
                  #
         
     | 
| 
      
 69 
     | 
    
         
            +
                  # @param code [String] the operation code
         
     | 
| 
      
 70 
     | 
    
         
            +
                  # @return [Symbol] operation as symbol
         
     | 
| 
      
 71 
     | 
    
         
            +
                  #
         
     | 
| 
      
 72 
     | 
    
         
            +
                  def self.rename_operation_to_symbol(code)
         
     | 
| 
      
 73 
     | 
    
         
            +
                    RENAME_OPERATIONS[code] || :unknown
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  # Get the staging status
         
     | 
| 
      
 77 
     | 
    
         
            +
                  #
         
     | 
| 
      
 78 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 79 
     | 
    
         
            +
                  #   entry.staging_status #=> :modified
         
     | 
| 
      
 80 
     | 
    
         
            +
                  #
         
     | 
| 
      
 81 
     | 
    
         
            +
                  # @return [Symbol, nil] staging status symbol or nil if not applicable
         
     | 
| 
      
 82 
     | 
    
         
            +
                  #
         
     | 
| 
      
 83 
     | 
    
         
            +
                  def index_status = nil
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  # Get the worktree status
         
     | 
| 
      
 86 
     | 
    
         
            +
                  #
         
     | 
| 
      
 87 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 88 
     | 
    
         
            +
                  #   entry.worktree_status #=> :unmodified
         
     | 
| 
      
 89 
     | 
    
         
            +
                  #
         
     | 
| 
      
 90 
     | 
    
         
            +
                  # @return [Symbol, nil] worktree status symbol or nil if not applicable
         
     | 
| 
      
 91 
     | 
    
         
            +
                  #
         
     | 
| 
      
 92 
     | 
    
         
            +
                  def worktree_status = nil
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                  # Is the entry an ignored file?
         
     | 
| 
      
 95 
     | 
    
         
            +
                  #
         
     | 
| 
      
 96 
     | 
    
         
            +
                  # * Ignored entries are not considered untracked
         
     | 
| 
      
 97 
     | 
    
         
            +
                  #
         
     | 
| 
      
 98 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 99 
     | 
    
         
            +
                  #   entry.ignored? #=> false
         
     | 
| 
      
 100 
     | 
    
         
            +
                  #
         
     | 
| 
      
 101 
     | 
    
         
            +
                  # @return [Boolean]
         
     | 
| 
      
 102 
     | 
    
         
            +
                  #
         
     | 
| 
      
 103 
     | 
    
         
            +
                  def ignored? = false
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                  # Is the entry an untracked file?
         
     | 
| 
      
 106 
     | 
    
         
            +
                  #
         
     | 
| 
      
 107 
     | 
    
         
            +
                  # * Ignored entries are not considered untracked
         
     | 
| 
      
 108 
     | 
    
         
            +
                  #
         
     | 
| 
      
 109 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 110 
     | 
    
         
            +
                  #   entry.ignored? #=> false
         
     | 
| 
      
 111 
     | 
    
         
            +
                  #
         
     | 
| 
      
 112 
     | 
    
         
            +
                  # @return [Boolean]
         
     | 
| 
      
 113 
     | 
    
         
            +
                  #
         
     | 
| 
      
 114 
     | 
    
         
            +
                  def untracked? = false
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  # Does the entry have unstaged changes in the worktree?
         
     | 
| 
      
 117 
     | 
    
         
            +
                  #
         
     | 
| 
      
 118 
     | 
    
         
            +
                  # * An entry can have both staged and unstaged changes
         
     | 
| 
      
 119 
     | 
    
         
            +
                  # * All untracked entries are considered unstaged
         
     | 
| 
      
 120 
     | 
    
         
            +
                  #
         
     | 
| 
      
 121 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 122 
     | 
    
         
            +
                  #   entry.ignored? #=> false
         
     | 
| 
      
 123 
     | 
    
         
            +
                  #
         
     | 
| 
      
 124 
     | 
    
         
            +
                  # @return [Boolean]
         
     | 
| 
      
 125 
     | 
    
         
            +
                  #
         
     | 
| 
      
 126 
     | 
    
         
            +
                  def unstaged? = false
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                  # Does the entry have staged changes in the index?
         
     | 
| 
      
 129 
     | 
    
         
            +
                  #
         
     | 
| 
      
 130 
     | 
    
         
            +
                  # * An entry can have both staged and unstaged changes
         
     | 
| 
      
 131 
     | 
    
         
            +
                  #
         
     | 
| 
      
 132 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 133 
     | 
    
         
            +
                  #   entry.ignored? #=> false
         
     | 
| 
      
 134 
     | 
    
         
            +
                  #
         
     | 
| 
      
 135 
     | 
    
         
            +
                  # @return [Boolean]
         
     | 
| 
      
 136 
     | 
    
         
            +
                  #
         
     | 
| 
      
 137 
     | 
    
         
            +
                  def staged? = false
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
                  # Does the entry have staged changes in the index with no unstaged changes?
         
     | 
| 
      
 140 
     | 
    
         
            +
                  #
         
     | 
| 
      
 141 
     | 
    
         
            +
                  # * An entry can have both staged and unstaged changes
         
     | 
| 
      
 142 
     | 
    
         
            +
                  #
         
     | 
| 
      
 143 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 144 
     | 
    
         
            +
                  #   entry.fully_staged? #=> false
         
     | 
| 
      
 145 
     | 
    
         
            +
                  #
         
     | 
| 
      
 146 
     | 
    
         
            +
                  # @return [Boolean]
         
     | 
| 
      
 147 
     | 
    
         
            +
                  #
         
     | 
| 
      
 148 
     | 
    
         
            +
                  def fully_staged? = staged? && !unstaged?
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                  # Does the entry represent a merge conflict?
         
     | 
| 
      
 151 
     | 
    
         
            +
                  #
         
     | 
| 
      
 152 
     | 
    
         
            +
                  # * Merge conflicts are not considered untracked, staged or unstaged
         
     | 
| 
      
 153 
     | 
    
         
            +
                  #
         
     | 
| 
      
 154 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 155 
     | 
    
         
            +
                  #   entry.conflict? #=> false
         
     | 
| 
      
 156 
     | 
    
         
            +
                  #
         
     | 
| 
      
 157 
     | 
    
         
            +
                  # @return [Boolean]
         
     | 
| 
      
 158 
     | 
    
         
            +
                  #
         
     | 
| 
      
 159 
     | 
    
         
            +
                  def unmerged? = false
         
     | 
| 
      
 160 
     | 
    
         
            +
                end
         
     | 
| 
      
 161 
     | 
    
         
            +
              end
         
     | 
| 
      
 162 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,44 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'entry'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module RubyGit
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Status
         
     | 
| 
      
 7 
     | 
    
         
            +
                # Represents an ignored file in git status
         
     | 
| 
      
 8 
     | 
    
         
            +
                #
         
     | 
| 
      
 9 
     | 
    
         
            +
                # @api public
         
     | 
| 
      
 10 
     | 
    
         
            +
                class IgnoredEntry < Entry
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # Parse a git status line to create an ignored entry
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #   IgnoredEntry.parse('!! lib/example.rb') #=> #<RubyGit::Status::IgnoredEntry:0x00000001046bd488 ...>
         
     | 
| 
      
 15 
     | 
    
         
            +
                  #
         
     | 
| 
      
 16 
     | 
    
         
            +
                  # @param line [String] line from git status
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # @return [RubyGit::Status::IgnoredEntry] parsed entry
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #
         
     | 
| 
      
 19 
     | 
    
         
            +
                  def self.parse(line)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    tokens = line.split(' ', 2)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    new(path: tokens[1])
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  # Initialize with the path
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #   IgnoredEntry.new(path: 'lib/example.rb')
         
     | 
| 
      
 28 
     | 
    
         
            +
                  #
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # @param path [String] file path
         
     | 
| 
      
 30 
     | 
    
         
            +
                  #
         
     | 
| 
      
 31 
     | 
    
         
            +
                  def initialize(path:)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    super(path)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  # Is the entry an ignored file?
         
     | 
| 
      
 36 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 37 
     | 
    
         
            +
                  #   entry.ignored? #=> false
         
     | 
| 
      
 38 
     | 
    
         
            +
                  # @return [Boolean]
         
     | 
| 
      
 39 
     | 
    
         
            +
                  def ignored?
         
     | 
| 
      
 40 
     | 
    
         
            +
                    true
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,207 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'entry'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative 'submodule_status'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module RubyGit
         
     | 
| 
      
 7 
     | 
    
         
            +
              module Status
         
     | 
| 
      
 8 
     | 
    
         
            +
                # Represents an ordinary changed file in git status
         
     | 
| 
      
 9 
     | 
    
         
            +
                #
         
     | 
| 
      
 10 
     | 
    
         
            +
                # @api public
         
     | 
| 
      
 11 
     | 
    
         
            +
                class OrdinaryEntry < Entry
         
     | 
| 
      
 12 
     | 
    
         
            +
                  # @attribute [r] index_status
         
     | 
| 
      
 13 
     | 
    
         
            +
                  #
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # The status in the staging area
         
     | 
| 
      
 15 
     | 
    
         
            +
                  #
         
     | 
| 
      
 16 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 17 
     | 
    
         
            +
                  #   entry.index_status #=> :modified
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # @return [Symbol] staging status
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 22 
     | 
    
         
            +
                  attr_reader :index_status
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  # @attribute [r] worktree_status
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # The status in the worktree
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 29 
     | 
    
         
            +
                  #   entry.worktree_status #=> :modified
         
     | 
| 
      
 30 
     | 
    
         
            +
                  #
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # @return [Symbol] worktree status
         
     | 
| 
      
 32 
     | 
    
         
            +
                  #
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 34 
     | 
    
         
            +
                  attr_reader :worktree_status
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  # @attribute [r] submodule_status
         
     | 
| 
      
 37 
     | 
    
         
            +
                  #
         
     | 
| 
      
 38 
     | 
    
         
            +
                  # The submodule status if the entry is a submodule
         
     | 
| 
      
 39 
     | 
    
         
            +
                  #
         
     | 
| 
      
 40 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 41 
     | 
    
         
            +
                  #   entry.submodule #=> 'N...'
         
     | 
| 
      
 42 
     | 
    
         
            +
                  #
         
     | 
| 
      
 43 
     | 
    
         
            +
                  # @return [SubmoduleStatus, nil] submodule status or nil if not a submodule
         
     | 
| 
      
 44 
     | 
    
         
            +
                  #
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 46 
     | 
    
         
            +
                  attr_reader :submodule_status
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  # @attribute [r] head_sha
         
     | 
| 
      
 49 
     | 
    
         
            +
                  #
         
     | 
| 
      
 50 
     | 
    
         
            +
                  # The SHA of this object in HEAD
         
     | 
| 
      
 51 
     | 
    
         
            +
                  #
         
     | 
| 
      
 52 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 53 
     | 
    
         
            +
                  #   entry.head_sha #=> 'd670460b4b4aece5915caf5c68d12f560a9fe3e4'
         
     | 
| 
      
 54 
     | 
    
         
            +
                  #
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # @return [String] SHA of this object in HEAD
         
     | 
| 
      
 56 
     | 
    
         
            +
                  #
         
     | 
| 
      
 57 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 58 
     | 
    
         
            +
                  attr_reader :head_sha
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  # @attribute [r] index_sha
         
     | 
| 
      
 61 
     | 
    
         
            +
                  #
         
     | 
| 
      
 62 
     | 
    
         
            +
                  # The SHA of this object in the index
         
     | 
| 
      
 63 
     | 
    
         
            +
                  #
         
     | 
| 
      
 64 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 65 
     | 
    
         
            +
                  #  entry.index_sha #=> 'd670460b4b4aece5915caf5c68d12f560a9fe3e4'
         
     | 
| 
      
 66 
     | 
    
         
            +
                  #
         
     | 
| 
      
 67 
     | 
    
         
            +
                  # @return [String] SHA of this object in the index
         
     | 
| 
      
 68 
     | 
    
         
            +
                  #
         
     | 
| 
      
 69 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 70 
     | 
    
         
            +
                  attr_reader :index_sha
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  # @attribute [r] head_mode
         
     | 
| 
      
 73 
     | 
    
         
            +
                  #
         
     | 
| 
      
 74 
     | 
    
         
            +
                  # The file mode in HEAD
         
     | 
| 
      
 75 
     | 
    
         
            +
                  #
         
     | 
| 
      
 76 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 77 
     | 
    
         
            +
                  #   entry.head_mode #=> 0o100644
         
     | 
| 
      
 78 
     | 
    
         
            +
                  #
         
     | 
| 
      
 79 
     | 
    
         
            +
                  # @return [Integer] file mode in HEAD
         
     | 
| 
      
 80 
     | 
    
         
            +
                  #
         
     | 
| 
      
 81 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 82 
     | 
    
         
            +
                  attr_reader :head_mode
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  # @attribute [r] index_mode
         
     | 
| 
      
 85 
     | 
    
         
            +
                  #
         
     | 
| 
      
 86 
     | 
    
         
            +
                  # The file mode in the index
         
     | 
| 
      
 87 
     | 
    
         
            +
                  #
         
     | 
| 
      
 88 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 89 
     | 
    
         
            +
                  #   entry.index_mode #=> 0o100644
         
     | 
| 
      
 90 
     | 
    
         
            +
                  #
         
     | 
| 
      
 91 
     | 
    
         
            +
                  # @return [Integer] file mode in the index
         
     | 
| 
      
 92 
     | 
    
         
            +
                  #
         
     | 
| 
      
 93 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 94 
     | 
    
         
            +
                  attr_reader :index_mode
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                  # @attribute [r] worktree_mode
         
     | 
| 
      
 97 
     | 
    
         
            +
                  #
         
     | 
| 
      
 98 
     | 
    
         
            +
                  # The file mode in the worktree
         
     | 
| 
      
 99 
     | 
    
         
            +
                  #
         
     | 
| 
      
 100 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 101 
     | 
    
         
            +
                  #   entry.worktree_mode #=> 0o100644
         
     | 
| 
      
 102 
     | 
    
         
            +
                  #
         
     | 
| 
      
 103 
     | 
    
         
            +
                  # @return [Integer] file mode in the worktree
         
     | 
| 
      
 104 
     | 
    
         
            +
                  #
         
     | 
| 
      
 105 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 106 
     | 
    
         
            +
                  attr_reader :worktree_mode
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                  # Parse an ordinary change line of git status output
         
     | 
| 
      
 109 
     | 
    
         
            +
                  #
         
     | 
| 
      
 110 
     | 
    
         
            +
                  # The line is expected to be in porcelain v2 format with NUL terminators.
         
     | 
| 
      
 111 
     | 
    
         
            +
                  #
         
     | 
| 
      
 112 
     | 
    
         
            +
                  # The format is as follows:
         
     | 
| 
      
 113 
     | 
    
         
            +
                  # 1 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <path>
         
     | 
| 
      
 114 
     | 
    
         
            +
                  #
         
     | 
| 
      
 115 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 116 
     | 
    
         
            +
                  #   line = '1 M N... 100644 100644 100644 d670460b4b4aece5915caf5c68d12f560a9fe3e4 ' \
         
     | 
| 
      
 117 
     | 
    
         
            +
                  #     \d670460b4b4aece5915caf5c68d12f560a9fe3e4 lib/example.rb'
         
     | 
| 
      
 118 
     | 
    
         
            +
                  #   OrdinaryEntry.parse(line) #=> #<RubyGit::Status::OrdinaryEntry:0x00000001046bd488 ...>
         
     | 
| 
      
 119 
     | 
    
         
            +
                  #
         
     | 
| 
      
 120 
     | 
    
         
            +
                  # @param line [String] line from git status
         
     | 
| 
      
 121 
     | 
    
         
            +
                  #
         
     | 
| 
      
 122 
     | 
    
         
            +
                  # @return [RubyGit::Status::OrdinaryEntry] parsed entry
         
     | 
| 
      
 123 
     | 
    
         
            +
                  #
         
     | 
| 
      
 124 
     | 
    
         
            +
                  def self.parse(line) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
         
     | 
| 
      
 125 
     | 
    
         
            +
                    tokens = line.split
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                    new(
         
     | 
| 
      
 128 
     | 
    
         
            +
                      index_status: Entry.status_to_symbol(tokens[1][0]),
         
     | 
| 
      
 129 
     | 
    
         
            +
                      worktree_status: Entry.status_to_symbol(tokens[1][1]),
         
     | 
| 
      
 130 
     | 
    
         
            +
                      submodule_status: SubmoduleStatus.parse(tokens[2]),
         
     | 
| 
      
 131 
     | 
    
         
            +
                      head_mode: Integer(tokens[3], 8),
         
     | 
| 
      
 132 
     | 
    
         
            +
                      index_mode: Integer(tokens[4], 8),
         
     | 
| 
      
 133 
     | 
    
         
            +
                      worktree_mode: Integer(tokens[5], 8),
         
     | 
| 
      
 134 
     | 
    
         
            +
                      head_sha: tokens[6],
         
     | 
| 
      
 135 
     | 
    
         
            +
                      index_sha: tokens[7],
         
     | 
| 
      
 136 
     | 
    
         
            +
                      path: tokens[8]
         
     | 
| 
      
 137 
     | 
    
         
            +
                    )
         
     | 
| 
      
 138 
     | 
    
         
            +
                  end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                  # Initialize a new ordinary entry
         
     | 
| 
      
 141 
     | 
    
         
            +
                  #
         
     | 
| 
      
 142 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 143 
     | 
    
         
            +
                  #   path = 'lib/example.rb'
         
     | 
| 
      
 144 
     | 
    
         
            +
                  #   index_status = :modified
         
     | 
| 
      
 145 
     | 
    
         
            +
                  #   worktree_status = :modified
         
     | 
| 
      
 146 
     | 
    
         
            +
                  #   submodule_status = nil
         
     | 
| 
      
 147 
     | 
    
         
            +
                  #   worktree_mode = 0o100644
         
     | 
| 
      
 148 
     | 
    
         
            +
                  #   index_mode = 0o100644
         
     | 
| 
      
 149 
     | 
    
         
            +
                  #   head_mode = 0o100644
         
     | 
| 
      
 150 
     | 
    
         
            +
                  #   head_sha = 'd670460b4b4aece5915caf5c68d12f560a9fe3e4'
         
     | 
| 
      
 151 
     | 
    
         
            +
                  #   index_sha = 'd670460b4b4aece5915caf5c68d12f560a9fe3e4'
         
     | 
| 
      
 152 
     | 
    
         
            +
                  #   OrdinaryEntry.new(
         
     | 
| 
      
 153 
     | 
    
         
            +
                  #     path:, index_status:, worktree_status:, submodule_status:,
         
     | 
| 
      
 154 
     | 
    
         
            +
                  #     worktree_mode:, index_mode:, head_mode:, head_sha:, index_sha:
         
     | 
| 
      
 155 
     | 
    
         
            +
                  #   )
         
     | 
| 
      
 156 
     | 
    
         
            +
                  #
         
     | 
| 
      
 157 
     | 
    
         
            +
                  # @param path [String] file path
         
     | 
| 
      
 158 
     | 
    
         
            +
                  # @param index_status [Symbol] status in staging area
         
     | 
| 
      
 159 
     | 
    
         
            +
                  # @param worktree_status [Symbol] status in worktree
         
     | 
| 
      
 160 
     | 
    
         
            +
                  # @param submodule_status [SubmoduleStatus, nil] submodule status if applicable
         
     | 
| 
      
 161 
     | 
    
         
            +
                  # @param worktree_mode [Integer] file mode in worktree
         
     | 
| 
      
 162 
     | 
    
         
            +
                  # @param index_mode [Integer] file mode in staging area
         
     | 
| 
      
 163 
     | 
    
         
            +
                  # @param head_mode [Integer] file mode in HEAD
         
     | 
| 
      
 164 
     | 
    
         
            +
                  #
         
     | 
| 
      
 165 
     | 
    
         
            +
                  def initialize( # rubocop:disable Metrics/ParameterLists
         
     | 
| 
      
 166 
     | 
    
         
            +
                    path:,
         
     | 
| 
      
 167 
     | 
    
         
            +
                    index_status:, worktree_status:,
         
     | 
| 
      
 168 
     | 
    
         
            +
                    submodule_status:,
         
     | 
| 
      
 169 
     | 
    
         
            +
                    head_mode:, index_mode:, worktree_mode:,
         
     | 
| 
      
 170 
     | 
    
         
            +
                    head_sha:, index_sha:
         
     | 
| 
      
 171 
     | 
    
         
            +
                  )
         
     | 
| 
      
 172 
     | 
    
         
            +
                    super(path)
         
     | 
| 
      
 173 
     | 
    
         
            +
                    @index_status = index_status
         
     | 
| 
      
 174 
     | 
    
         
            +
                    @worktree_status = worktree_status
         
     | 
| 
      
 175 
     | 
    
         
            +
                    @submodule_status = submodule_status
         
     | 
| 
      
 176 
     | 
    
         
            +
                    @worktree_mode = worktree_mode
         
     | 
| 
      
 177 
     | 
    
         
            +
                    @index_mode = index_mode
         
     | 
| 
      
 178 
     | 
    
         
            +
                    @head_mode = head_mode
         
     | 
| 
      
 179 
     | 
    
         
            +
                    @head_sha = head_sha
         
     | 
| 
      
 180 
     | 
    
         
            +
                    @index_sha = index_sha
         
     | 
| 
      
 181 
     | 
    
         
            +
                  end
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
                  # Does the entry have unstaged changes in the worktree?
         
     | 
| 
      
 184 
     | 
    
         
            +
                  #
         
     | 
| 
      
 185 
     | 
    
         
            +
                  # * An entry can have both staged and unstaged changes
         
     | 
| 
      
 186 
     | 
    
         
            +
                  # * All untracked entries are considered unstaged
         
     | 
| 
      
 187 
     | 
    
         
            +
                  #
         
     | 
| 
      
 188 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 189 
     | 
    
         
            +
                  #   entry.ignored? #=> false
         
     | 
| 
      
 190 
     | 
    
         
            +
                  # @return [Boolean]
         
     | 
| 
      
 191 
     | 
    
         
            +
                  def unstaged?
         
     | 
| 
      
 192 
     | 
    
         
            +
                    worktree_status != :unmodified
         
     | 
| 
      
 193 
     | 
    
         
            +
                  end
         
     | 
| 
      
 194 
     | 
    
         
            +
             
     | 
| 
      
 195 
     | 
    
         
            +
                  # Does the entry have staged changes in the index?
         
     | 
| 
      
 196 
     | 
    
         
            +
                  #
         
     | 
| 
      
 197 
     | 
    
         
            +
                  # * An entry can have both staged and unstaged changes
         
     | 
| 
      
 198 
     | 
    
         
            +
                  #
         
     | 
| 
      
 199 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 200 
     | 
    
         
            +
                  #   entry.ignored? #=> false
         
     | 
| 
      
 201 
     | 
    
         
            +
                  # @return [Boolean]
         
     | 
| 
      
 202 
     | 
    
         
            +
                  def staged?
         
     | 
| 
      
 203 
     | 
    
         
            +
                    index_status != :unmodified
         
     | 
| 
      
 204 
     | 
    
         
            +
                  end
         
     | 
| 
      
 205 
     | 
    
         
            +
                end
         
     | 
| 
      
 206 
     | 
    
         
            +
              end
         
     | 
| 
      
 207 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,203 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'report'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative 'branch'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative 'stash'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require_relative 'entry'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require_relative 'ordinary_entry'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require_relative 'renamed_entry'
         
     | 
| 
      
 9 
     | 
    
         
            +
            require_relative 'unmerged_entry'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require_relative 'ignored_entry'
         
     | 
| 
      
 11 
     | 
    
         
            +
            require_relative 'untracked_entry'
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            module RubyGit
         
     | 
| 
      
 14 
     | 
    
         
            +
              module Status
         
     | 
| 
      
 15 
     | 
    
         
            +
                # Parses the git status porcelain v2 format output
         
     | 
| 
      
 16 
     | 
    
         
            +
                #
         
     | 
| 
      
 17 
     | 
    
         
            +
                # git status --porcelain=v2 -z \
         
     | 
| 
      
 18 
     | 
    
         
            +
                #   --untracked-files --ignored-files --renames \
         
     | 
| 
      
 19 
     | 
    
         
            +
                #   --ahead-behind --branch --show-stash
         
     | 
| 
      
 20 
     | 
    
         
            +
                #
         
     | 
| 
      
 21 
     | 
    
         
            +
                # @api public
         
     | 
| 
      
 22 
     | 
    
         
            +
                class Parser
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # Parse the git status output and return a report object
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #   status_output = `git status -u --porcelain=v2 --renames --branch --show-stash -z`
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #   report = RubyGit::Status::Parser.parse(status_output) #=> #<RubyGit::Status::Report>
         
     | 
| 
      
 28 
     | 
    
         
            +
                  #
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # @param status_output [String] raw git status output
         
     | 
| 
      
 30 
     | 
    
         
            +
                  #
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # @return [RubyGit::Status::Report] parsed status report
         
     | 
| 
      
 32 
     | 
    
         
            +
                  #
         
     | 
| 
      
 33 
     | 
    
         
            +
                  def self.parse(status_output)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    new(status_output).parse
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  # Create a new status output parser
         
     | 
| 
      
 38 
     | 
    
         
            +
                  #
         
     | 
| 
      
 39 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 40 
     | 
    
         
            +
                  #    status_output = `git status -u --porcelain=v2 --renames --branch --show-stash -z`
         
     | 
| 
      
 41 
     | 
    
         
            +
                  #    parser = RubyGit::Status::Parser.new(status_output)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  #
         
     | 
| 
      
 43 
     | 
    
         
            +
                  # @param status_output [String] raw git status output
         
     | 
| 
      
 44 
     | 
    
         
            +
                  #
         
     | 
| 
      
 45 
     | 
    
         
            +
                  def initialize(status_output)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    @status_output = status_output
         
     | 
| 
      
 47 
     | 
    
         
            +
                    @entries = []
         
     | 
| 
      
 48 
     | 
    
         
            +
                    @branch = nil
         
     | 
| 
      
 49 
     | 
    
         
            +
                    @stash = Stash.new(0)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  # Parse the git status output
         
     | 
| 
      
 53 
     | 
    
         
            +
                  #
         
     | 
| 
      
 54 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 55 
     | 
    
         
            +
                  #   status_output = `git status -u --porcelain=v2 --renames --branch --show-stash -z`
         
     | 
| 
      
 56 
     | 
    
         
            +
                  #   parser = RubyGit::Status::Parser.new(status_output)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  #   parser.parse #=> #<RubyGit::Status::Report>
         
     | 
| 
      
 58 
     | 
    
         
            +
                  #
         
     | 
| 
      
 59 
     | 
    
         
            +
                  # @return [RubyGit::Status::Report] parsed status report
         
     | 
| 
      
 60 
     | 
    
         
            +
                  #
         
     | 
| 
      
 61 
     | 
    
         
            +
                  def parse
         
     | 
| 
      
 62 
     | 
    
         
            +
                    process_lines(status_output_lines)
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                    Report.new(@branch, @stash, @entries)
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  # Define the parser for each line type
         
     | 
| 
      
 68 
     | 
    
         
            +
                  LINE_PARSER_FACTORY = {
         
     | 
| 
      
 69 
     | 
    
         
            +
                    '1' => ->(line) { OrdinaryEntry.parse(line) },
         
     | 
| 
      
 70 
     | 
    
         
            +
                    '2' => ->(line) { RenamedEntry.parse(line) },
         
     | 
| 
      
 71 
     | 
    
         
            +
                    'u' => ->(line) { UnmergedEntry.parse(line) },
         
     | 
| 
      
 72 
     | 
    
         
            +
                    '!' => ->(line) { IgnoredEntry.parse(line) },
         
     | 
| 
      
 73 
     | 
    
         
            +
                    '?' => ->(line) { UntrackedEntry.parse(line) }
         
     | 
| 
      
 74 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  private
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                  # Process the split lines of git status output
         
     | 
| 
      
 79 
     | 
    
         
            +
                  #
         
     | 
| 
      
 80 
     | 
    
         
            +
                  # @param lines [Array<String>] lines from git status output
         
     | 
| 
      
 81 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 82 
     | 
    
         
            +
                  #
         
     | 
| 
      
 83 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 84 
     | 
    
         
            +
                  def process_lines(lines)
         
     | 
| 
      
 85 
     | 
    
         
            +
                    # Use the LINE_PARSER_FACTORY to parse each line
         
     | 
| 
      
 86 
     | 
    
         
            +
                    # based on the first character like the code below
         
     | 
| 
      
 87 
     | 
    
         
            +
                    lines.each do |line|
         
     | 
| 
      
 88 
     | 
    
         
            +
                      next if line.empty?
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                      if line.start_with?('#')
         
     | 
| 
      
 91 
     | 
    
         
            +
                        parse_header_line(line)
         
     | 
| 
      
 92 
     | 
    
         
            +
                      else
         
     | 
| 
      
 93 
     | 
    
         
            +
                        @entries << LINE_PARSER_FACTORY[line[0]].call(line)
         
     | 
| 
      
 94 
     | 
    
         
            +
                      end
         
     | 
| 
      
 95 
     | 
    
         
            +
                    end
         
     | 
| 
      
 96 
     | 
    
         
            +
                  end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                  # Parse a header line (starts with #)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  #
         
     | 
| 
      
 100 
     | 
    
         
            +
                  # @param line [String] header line from git status
         
     | 
| 
      
 101 
     | 
    
         
            +
                  #
         
     | 
| 
      
 102 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 103 
     | 
    
         
            +
                  #
         
     | 
| 
      
 104 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 105 
     | 
    
         
            +
                  def parse_header_line(line)
         
     | 
| 
      
 106 
     | 
    
         
            +
                    tokens = line.split
         
     | 
| 
      
 107 
     | 
    
         
            +
                    header_type = tokens[1]
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                    process_header(header_type, tokens)
         
     | 
| 
      
 110 
     | 
    
         
            +
                  end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                  # Split the status output into lines
         
     | 
| 
      
 113 
     | 
    
         
            +
                  #
         
     | 
| 
      
 114 
     | 
    
         
            +
                  # This is more complicated than a simple split because
         
     | 
| 
      
 115 
     | 
    
         
            +
                  # the lines are NUL terminated but some entries have
         
     | 
| 
      
 116 
     | 
    
         
            +
                  # also use NUL as a field separator (e.g. renamed entries).
         
     | 
| 
      
 117 
     | 
    
         
            +
                  #
         
     | 
| 
      
 118 
     | 
    
         
            +
                  # @return [Array<String>] split lines
         
     | 
| 
      
 119 
     | 
    
         
            +
                  #
         
     | 
| 
      
 120 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 121 
     | 
    
         
            +
                  #
         
     | 
| 
      
 122 
     | 
    
         
            +
                  def status_output_lines
         
     | 
| 
      
 123 
     | 
    
         
            +
                    parts = @status_output.split("\u0000")
         
     | 
| 
      
 124 
     | 
    
         
            +
                    [].tap do |lines|
         
     | 
| 
      
 125 
     | 
    
         
            +
                      until parts.empty?
         
     | 
| 
      
 126 
     | 
    
         
            +
                        next_entry_type = parts.first[0]
         
     | 
| 
      
 127 
     | 
    
         
            +
                        lines << parts.shift
         
     | 
| 
      
 128 
     | 
    
         
            +
                        lines[-1] = "#{lines[-1]}\u0000#{parts.shift}" if next_entry_type == '2'
         
     | 
| 
      
 129 
     | 
    
         
            +
                      end
         
     | 
| 
      
 130 
     | 
    
         
            +
                    end
         
     | 
| 
      
 131 
     | 
    
         
            +
                  end
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                  # Info from the status_output about the branch
         
     | 
| 
      
 134 
     | 
    
         
            +
                  # @return [Branch]
         
     | 
| 
      
 135 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 136 
     | 
    
         
            +
                  def branch = @branch ||= Branch.new
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                  # Info about the stashes about stashes
         
     | 
| 
      
 139 
     | 
    
         
            +
                  # @return [Stash]
         
     | 
| 
      
 140 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 141 
     | 
    
         
            +
                  attr_reader :stash
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                  # Branch name setter that handles detached HEAD
         
     | 
| 
      
 144 
     | 
    
         
            +
                  # @param name [String] branch name
         
     | 
| 
      
 145 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 146 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 147 
     | 
    
         
            +
                  def branch_name=(name)
         
     | 
| 
      
 148 
     | 
    
         
            +
                    branch.name = name unless name == '(detached)'
         
     | 
| 
      
 149 
     | 
    
         
            +
                  end
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                  # Branch oid setter that handles initial commit
         
     | 
| 
      
 152 
     | 
    
         
            +
                  # @param oid [String] branch oid
         
     | 
| 
      
 153 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 154 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 155 
     | 
    
         
            +
                  def branch_oid=(oid)
         
     | 
| 
      
 156 
     | 
    
         
            +
                    branch.oid = oid unless oid == '(initial)'
         
     | 
| 
      
 157 
     | 
    
         
            +
                  end
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                  # Branch upstream setter
         
     | 
| 
      
 160 
     | 
    
         
            +
                  # @param upstream [String] branch upstream
         
     | 
| 
      
 161 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 162 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 163 
     | 
    
         
            +
                  def branch_upstream=(upstream)
         
     | 
| 
      
 164 
     | 
    
         
            +
                    branch.upstream = upstream
         
     | 
| 
      
 165 
     | 
    
         
            +
                  end
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                  # Branch ahead setter that converts from string to integer
         
     | 
| 
      
 168 
     | 
    
         
            +
                  # @param ahead [String] branch ahead
         
     | 
| 
      
 169 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 170 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 171 
     | 
    
         
            +
                  def branch_ahead=(ahead)
         
     | 
| 
      
 172 
     | 
    
         
            +
                    branch.ahead = ahead.sub('+', '').to_i
         
     | 
| 
      
 173 
     | 
    
         
            +
                  end
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                  # Branch behind setter that converts from string to integer
         
     | 
| 
      
 176 
     | 
    
         
            +
                  # @param behind [String] branch behind
         
     | 
| 
      
 177 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 178 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 179 
     | 
    
         
            +
                  def branch_behind=(behind)
         
     | 
| 
      
 180 
     | 
    
         
            +
                    branch.behind = behind.sub('-', '').to_i
         
     | 
| 
      
 181 
     | 
    
         
            +
                  end
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
                  # Process a specific header type with its tokens
         
     | 
| 
      
 184 
     | 
    
         
            +
                  #
         
     | 
| 
      
 185 
     | 
    
         
            +
                  # @param header_type [String] type of header
         
     | 
| 
      
 186 
     | 
    
         
            +
                  # @param tokens [Array<String>] tokens from header line
         
     | 
| 
      
 187 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 188 
     | 
    
         
            +
                  #
         
     | 
| 
      
 189 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 190 
     | 
    
         
            +
                  def process_header(header_type, tokens)
         
     | 
| 
      
 191 
     | 
    
         
            +
                    case header_type
         
     | 
| 
      
 192 
     | 
    
         
            +
                    when 'branch.head' then self.branch_name = tokens[2]
         
     | 
| 
      
 193 
     | 
    
         
            +
                    when 'branch.oid' then self.branch_oid = tokens[2]
         
     | 
| 
      
 194 
     | 
    
         
            +
                    when 'branch.upstream' then self.branch_upstream = tokens[2]
         
     | 
| 
      
 195 
     | 
    
         
            +
                    when 'branch.ab'
         
     | 
| 
      
 196 
     | 
    
         
            +
                      self.branch_ahead = tokens[2]
         
     | 
| 
      
 197 
     | 
    
         
            +
                      self.branch_behind = tokens[3]
         
     | 
| 
      
 198 
     | 
    
         
            +
                    when 'stash' then stash.count = tokens[2].to_i
         
     | 
| 
      
 199 
     | 
    
         
            +
                    end
         
     | 
| 
      
 200 
     | 
    
         
            +
                  end
         
     | 
| 
      
 201 
     | 
    
         
            +
                end
         
     | 
| 
      
 202 
     | 
    
         
            +
              end
         
     | 
| 
      
 203 
     | 
    
         
            +
            end
         
     |