git-status-tree 1.0.0
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 +7 -0
- data/bin/git-status-tree +6 -0
- data/bin/git_add_alias_tree +12 -0
- data/bin/git_remove_alias_tree +7 -0
- data/ext/git_tree/Makefile +4 -0
- data/ext/git_tree/extconf.rb +15 -0
- data/lib/bash_color.rb +34 -0
- data/lib/node.rb +208 -0
- data/lib/nodes_collection.rb +179 -0
- data/lib/rubygems_plugin.rb +5 -0
- data/src/git-status-tree.rb +16 -0
- data/src/git_status_tree.rb +25 -0
- metadata +58 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: 4fdc77cb8d05dbec437b7ca6aaaf46bff71d2d35fa6ca2c1435c40fa1871f0d6
         | 
| 4 | 
            +
              data.tar.gz: cfe0d6434eacb99525c1116668a6f251ef23b50253b45bdf413837dffbf609c4
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 4f15db6fa9f6bf045faeac4f86c4f5131f0323f5771182d2d1f81fcde6cf068176cf47a8d71a2f9df3a050137b64b99788a5d24f3554fab014b51a0edc311344
         | 
| 7 | 
            +
              data.tar.gz: 5f61fbea0eaf0fc67fb82fc69421aac9c3faf498759e621b54298e1fffd560e91f5cdce7539bfca151b155b4b09bead7f8cfe629434613c9d35037cdfdde52dc
         | 
    
        data/bin/git-status-tree
    ADDED
    
    
| @@ -0,0 +1,12 @@ | |
| 1 | 
            +
            #!/bin/bash
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            pushd `dirname $0` > /dev/null
         | 
| 4 | 
            +
            BIN_DIR=`pwd`
         | 
| 5 | 
            +
            popd > /dev/null
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            git config --global status-tree.indent 4
         | 
| 8 | 
            +
            git config --global alias.tree "!sh -c \"$BIN_DIR/git-status-tree\""
         | 
| 9 | 
            +
            echo '#############################'
         | 
| 10 | 
            +
            echo '# "git tree" has been added #'
         | 
| 11 | 
            +
            echo '# Run: git tree             #'
         | 
| 12 | 
            +
            echo '#############################'
         | 
| @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            require 'mkmf'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            find_executable('bash')
         | 
| 4 | 
            +
            find_executable('git')
         | 
| 5 | 
            +
            find_executable('make')
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            # Trick Rubygems into thinking the generated Makefile was executed
         | 
| 8 | 
            +
            compile = File.join(Dir.pwd, 'git_tree.' + RbConfig::CONFIG['DLEXT'])
         | 
| 9 | 
            +
            File.open(compile, "w") {}
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            # Install "git tree"
         | 
| 12 | 
            +
            puts `../../bin/git_add_alias_tree`
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            # Trick Rubygems into thinking the Makefile was executed
         | 
| 15 | 
            +
            $makefile_created = true
         | 
    
        data/lib/bash_color.rb
    ADDED
    
    | @@ -0,0 +1,34 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module BashColor
         | 
| 4 | 
            +
              NONE = "\033[0m"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              K = "\033[0;30m"  # black
         | 
| 7 | 
            +
              R = "\033[0;31m"  # red
         | 
| 8 | 
            +
              G = "\033[0;32m"  # green
         | 
| 9 | 
            +
              Y = "\033[0;33m"  # yellow
         | 
| 10 | 
            +
              B = "\033[0;34m"  # blue
         | 
| 11 | 
            +
              M = "\033[0;35m"  # magenta
         | 
| 12 | 
            +
              C = "\033[0;36m"  # cyan
         | 
| 13 | 
            +
              W = "\033[0;37m"  # white
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              # emphasized (bolded) colors
         | 
| 16 | 
            +
              EMK = "\033[1;30m"
         | 
| 17 | 
            +
              EMR = "\033[1;31m"
         | 
| 18 | 
            +
              EMG = "\033[1;32m"
         | 
| 19 | 
            +
              EMY = "\033[1;33m"
         | 
| 20 | 
            +
              EMB = "\033[1;34m"
         | 
| 21 | 
            +
              EMM = "\033[1;35m"
         | 
| 22 | 
            +
              EMC = "\033[1;36m"
         | 
| 23 | 
            +
              EMW = "\033[1;37m"
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              # background colors
         | 
| 26 | 
            +
              BGK = "\033[40m"
         | 
| 27 | 
            +
              BGR = "\033[41m"
         | 
| 28 | 
            +
              BGG = "\033[42m"
         | 
| 29 | 
            +
              BGY = "\033[43m"
         | 
| 30 | 
            +
              BGB = "\033[44m"
         | 
| 31 | 
            +
              BGM = "\033[45m"
         | 
| 32 | 
            +
              BGC = "\033[46m"
         | 
| 33 | 
            +
              BGW = "\033[47m"
         | 
| 34 | 
            +
            end
         | 
    
        data/lib/node.rb
    ADDED
    
    | @@ -0,0 +1,208 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class NodeNameError < StandardError; end
         | 
| 4 | 
            +
            class NodeChildrenError < StandardError; end
         | 
| 5 | 
            +
            class NodeTypeError < StandardError; end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            # A Node represents a file or directory in the git-status-tree
         | 
| 8 | 
            +
            class Node
         | 
| 9 | 
            +
              class << self
         | 
| 10 | 
            +
                attr_accessor :indent
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              attr_accessor :status, :name, :children
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              def initialize(name, children = nil, status = nil)
         | 
| 16 | 
            +
                self.class.indent ||= 4
         | 
| 17 | 
            +
                msg = '"name" must be a String.'
         | 
| 18 | 
            +
                raise NodeNameError, msg unless name.is_a? String
         | 
| 19 | 
            +
                msg = '"name" must have at least one character.'
         | 
| 20 | 
            +
                raise NodeNameError, msg if name.empty?
         | 
| 21 | 
            +
                msg = '"name" must not contain "/", use create_from_string.'
         | 
| 22 | 
            +
                raise NodeNameError, msg if name =~ /\//
         | 
| 23 | 
            +
                msg = '"children" must be a NodesCollection or nil.'
         | 
| 24 | 
            +
                valid_nodes_collection = children.nil? || children.is_a?(NodesCollection)
         | 
| 25 | 
            +
                raise NodeChildrenError, msg unless valid_nodes_collection
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                @name = name
         | 
| 28 | 
            +
                @children = children
         | 
| 29 | 
            +
                @status = status || []
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def self.create_from_string(gs_porcelain)
         | 
| 33 | 
            +
                msg = '"str_node" must be String.'
         | 
| 34 | 
            +
                raise NodeTypeError, msg unless gs_porcelain.is_a? String
         | 
| 35 | 
            +
                raise NodeNameError, '"str_node" too short.' if gs_porcelain.length < 4
         | 
| 36 | 
            +
                status = if gs_porcelain[1..1] == ' '
         | 
| 37 | 
            +
                           gs_porcelain[0..0] + '+'
         | 
| 38 | 
            +
                         else
         | 
| 39 | 
            +
                           gs_porcelain[1..1]
         | 
| 40 | 
            +
                         end
         | 
| 41 | 
            +
                path = './' + gs_porcelain[3..-1]
         | 
| 42 | 
            +
                ary_nodes = path.split(/\//)
         | 
| 43 | 
            +
                name = ary_nodes.shift
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                if ary_nodes.any?
         | 
| 46 | 
            +
                  children = NodesCollection.create_from_array(ary_nodes, status)
         | 
| 47 | 
            +
                  node = self.new(name, children)
         | 
| 48 | 
            +
                else
         | 
| 49 | 
            +
                  node = self.new(name, nil, status)
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                node
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              def self.instances?
         | 
| 56 | 
            +
                lambda { |node| node.is_a?(Node) }
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
              def to_primitive
         | 
| 60 | 
            +
                if dir?
         | 
| 61 | 
            +
                  {name => children.to_primitive}
         | 
| 62 | 
            +
                else
         | 
| 63 | 
            +
                  name
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              def file?
         | 
| 68 | 
            +
                children.nil?
         | 
| 69 | 
            +
              end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              def dir?
         | 
| 72 | 
            +
                !file?
         | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
              def valid?
         | 
| 76 | 
            +
                return valid_dir? if dir?
         | 
| 77 | 
            +
                return valid_file? if file?
         | 
| 78 | 
            +
                false
         | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
              # @return [NodesCollection]
         | 
| 82 | 
            +
              def +(other)
         | 
| 83 | 
            +
                raise 'not valid' unless self.valid? && other.valid?
         | 
| 84 | 
            +
                raise 'not a ' + self.class.to_s unless other.is_a?(self.class)
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                tmp_children = [self.children, other.children].compact.inject(&:+)
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                NodesCollection.new([self.class.new(self.name, tmp_children)])
         | 
| 89 | 
            +
              end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
              def <=>(other)
         | 
| 92 | 
            +
                return (self.name <=> other.name) if self.file? == other.file?
         | 
| 93 | 
            +
                return -1 if (self.dir? && other.file?)
         | 
| 94 | 
            +
                return 1 if (self.file? && other.dir?)
         | 
| 95 | 
            +
                0
         | 
| 96 | 
            +
              end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
              def to_tree_s(depth = 0, open_parents = [0], last = true)
         | 
| 99 | 
            +
                open_parents << depth
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                pre = pre_tree(depth, open_parents, last)
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                color_name = ''
         | 
| 104 | 
            +
                if dir?
         | 
| 105 | 
            +
                  color_name += BashColor::EMB + name
         | 
| 106 | 
            +
                else #file?
         | 
| 107 | 
            +
                  if staged?
         | 
| 108 | 
            +
                    color_name += BashColor::G
         | 
| 109 | 
            +
                  else
         | 
| 110 | 
            +
                    color_name += BashColor::R
         | 
| 111 | 
            +
                  end
         | 
| 112 | 
            +
                  color_name += name + ' (' + status + ')'
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
                color_name +=  BashColor::NONE
         | 
| 115 | 
            +
             | 
| 116 | 
            +
             | 
| 117 | 
            +
                str_tree = pre + color_name + "\n"
         | 
| 118 | 
            +
                str_tree << children.to_tree_s(depth + 1, open_parents) if children
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                str_tree
         | 
| 121 | 
            +
              end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
              #   'M'  modified
         | 
| 124 | 
            +
              def modified?
         | 
| 125 | 
            +
                self.status.include?('M')
         | 
| 126 | 
            +
              end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
              #   'A'  added
         | 
| 129 | 
            +
              def added?
         | 
| 130 | 
            +
                self.status.include?('A')
         | 
| 131 | 
            +
              end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
              #   'D'  deleted
         | 
| 134 | 
            +
              def deleted?
         | 
| 135 | 
            +
                self.status.include?('D')
         | 
| 136 | 
            +
              end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
              #   'R'  renamed
         | 
| 139 | 
            +
              def renamed?
         | 
| 140 | 
            +
                self.status.include?('R')
         | 
| 141 | 
            +
              end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
              #   'C'  copied
         | 
| 144 | 
            +
              def copied?
         | 
| 145 | 
            +
                self.status.include?('C')
         | 
| 146 | 
            +
              end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
              #   'U'  updated but unmerged
         | 
| 149 | 
            +
              def unmerged?
         | 
| 150 | 
            +
                self.status.include?('U')
         | 
| 151 | 
            +
              end
         | 
| 152 | 
            +
             | 
| 153 | 
            +
              #   '?' new
         | 
| 154 | 
            +
              def new?
         | 
| 155 | 
            +
                self.status.include?('?')
         | 
| 156 | 
            +
              end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
              #   '+' staged
         | 
| 159 | 
            +
              def staged?
         | 
| 160 | 
            +
                self.status.include?('+')
         | 
| 161 | 
            +
              end
         | 
| 162 | 
            +
             | 
| 163 | 
            +
              private
         | 
| 164 | 
            +
              # Has a valid name?
         | 
| 165 | 
            +
              def name_valid?
         | 
| 166 | 
            +
                name &&
         | 
| 167 | 
            +
                  name.is_a?(String) &&
         | 
| 168 | 
            +
                  (name.length > 0) &&
         | 
| 169 | 
            +
                  name.match(/\//).nil?
         | 
| 170 | 
            +
              end
         | 
| 171 | 
            +
             | 
| 172 | 
            +
              # Is a valid dir?
         | 
| 173 | 
            +
              def valid_dir?
         | 
| 174 | 
            +
                name_valid? &&
         | 
| 175 | 
            +
                  children.is_a?(NodesCollection) &&
         | 
| 176 | 
            +
                  children.valid?
         | 
| 177 | 
            +
              end
         | 
| 178 | 
            +
             | 
| 179 | 
            +
              # Is a valid file?
         | 
| 180 | 
            +
              def valid_file?
         | 
| 181 | 
            +
                name_valid? &&
         | 
| 182 | 
            +
                  children.nil?
         | 
| 183 | 
            +
              end
         | 
| 184 | 
            +
             | 
| 185 | 
            +
              def pre_tree(depth, open_parents, last)
         | 
| 186 | 
            +
                if depth == 0
         | 
| 187 | 
            +
                  ''
         | 
| 188 | 
            +
                elsif depth > 0
         | 
| 189 | 
            +
                  pre_ary = Array.new(depth).fill('    ')
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                  indent = self.class.indent - 2
         | 
| 192 | 
            +
                  open_parents.each do |idx|
         | 
| 193 | 
            +
                    pre_ary[idx] = '│' + (' ' * indent) + ' ' if pre_ary[idx] == '    '
         | 
| 194 | 
            +
                  end
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                  if last
         | 
| 197 | 
            +
                    pre_ary[-1] = '└'
         | 
| 198 | 
            +
                    open_parents.delete(depth-1)
         | 
| 199 | 
            +
                  else
         | 
| 200 | 
            +
                    pre_ary[-1] = '├'
         | 
| 201 | 
            +
                  end
         | 
| 202 | 
            +
                  pre_ary[-1] += ('─' * indent) + ' '
         | 
| 203 | 
            +
             | 
| 204 | 
            +
             | 
| 205 | 
            +
                  pre_ary * ''
         | 
| 206 | 
            +
                end
         | 
| 207 | 
            +
              end
         | 
| 208 | 
            +
            end
         | 
| @@ -0,0 +1,179 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # error class for invalid NodeCollection types
         | 
| 4 | 
            +
            class NodesCollectionTypeError < StandardError;
         | 
| 5 | 
            +
            end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            # collection of nodes
         | 
| 8 | 
            +
            class NodesCollection
         | 
| 9 | 
            +
              attr_accessor :nodes
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              def self.create_from_string(str_nodes, status = '   ')
         | 
| 12 | 
            +
                msg = '"str_nodes" must be String.'
         | 
| 13 | 
            +
                raise NodesCollectionTypeError, msg unless str_nodes.is_a? String
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                ary_nodes = str_nodes.split(/\//)
         | 
| 16 | 
            +
                create_from_valid_array(ary_nodes, status)
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              def self.create_from_array(ary_nodes, status)
         | 
| 20 | 
            +
                ary_nodes = [ary_nodes].flatten(1)
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                msg = '"ary_nodes" must only contain Strings.'
         | 
| 23 | 
            +
                are_strings = lambda { |node| node.is_a?(String) }
         | 
| 24 | 
            +
                raise NodesCollectionTypeError, msg unless ary_nodes.all? &are_strings
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                create_from_valid_array(ary_nodes, status)
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              def self.new_from_nodes_array(nodes)
         | 
| 30 | 
            +
                raise msg unless nodes.all? &Node.instances?
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                all_nodes = nodes.group_by(&:name)
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                plain_nodes = []
         | 
| 35 | 
            +
                all_nodes.each { |_, nodes| plain_nodes << nodes if nodes.length == 1 }
         | 
| 36 | 
            +
                plain_nodes.flatten!(1)
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                merged_nodes = []
         | 
| 39 | 
            +
                all_nodes.each do |_, nodes|
         | 
| 40 | 
            +
                  if nodes.length == 2
         | 
| 41 | 
            +
                    merged_nodes << (nodes[0] + nodes[1]).nodes[0]
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                self.new(plain_nodes + merged_nodes)
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              def self.create_from_valid_array(ary_nodes, status)
         | 
| 49 | 
            +
                name = ary_nodes.shift
         | 
| 50 | 
            +
                node = if ary_nodes.any?
         | 
| 51 | 
            +
                         children = create_from_valid_array(ary_nodes, status)
         | 
| 52 | 
            +
                         Node.new(name, children)
         | 
| 53 | 
            +
                       else
         | 
| 54 | 
            +
                         Node.new(name, nil, status)
         | 
| 55 | 
            +
                       end
         | 
| 56 | 
            +
                self.new([node])
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
              private_class_method :create_from_valid_array
         | 
| 60 | 
            +
             | 
| 61 | 
            +
              def initialize(nodes = [])
         | 
| 62 | 
            +
                nodes = [nodes].flatten(1)
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                msg = '"nodes" must only contain Nodes.'
         | 
| 65 | 
            +
                are_nodes = lambda { |node| node.is_a?(Node) }
         | 
| 66 | 
            +
                raise NodesCollectionTypeError, msg unless nodes.all? &are_nodes
         | 
| 67 | 
            +
                @nodes = nodes
         | 
| 68 | 
            +
              end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
              # @return [NodesCollection]
         | 
| 71 | 
            +
              def +(other)
         | 
| 72 | 
            +
                unless other.is_a?(Node) || other.is_a?(self.class)
         | 
| 73 | 
            +
                  raise 'not a Node or NodesCollection'
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                all_nodes = merge_nodes_with other
         | 
| 77 | 
            +
                all_dirs = all_nodes.select(&:dir?)
         | 
| 78 | 
            +
                all_files = all_nodes.select(&:file?)
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                dirs_collection = self.class.new_from_nodes_array all_dirs
         | 
| 81 | 
            +
                files_collection = self.class.new all_files
         | 
| 82 | 
            +
             | 
| 83 | 
            +
             | 
| 84 | 
            +
                dir_nodes = dirs_collection.sort!
         | 
| 85 | 
            +
                file_nodes = files_collection.sort!
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                self.class.new(dir_nodes + file_nodes)
         | 
| 88 | 
            +
              end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
              # @return [Integer]
         | 
| 91 | 
            +
              def <=>(other)
         | 
| 92 | 
            +
                self.to_primitive <=> other.to_primitive
         | 
| 93 | 
            +
              end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
              # @return [Array<Node>]
         | 
| 96 | 
            +
              def nodes_not_in(other)
         | 
| 97 | 
            +
                self_names = self.nodes.map(&:name)
         | 
| 98 | 
            +
                other_names = other.nodes.map(&:name)
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                self_only_names = self_names - (self_names & other_names)
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                self.nodes.select { |node| self_only_names.include?(node.name) }
         | 
| 103 | 
            +
              end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
              # @return [Array<Node>]
         | 
| 106 | 
            +
              def merge_common_nodes_with(other)
         | 
| 107 | 
            +
                self_names = self.nodes.map(&:name)
         | 
| 108 | 
            +
                other_names = other.nodes.map(&:name)
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                common_names = self_names & other_names
         | 
| 111 | 
            +
                all_nodes = self.nodes + other.nodes
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                common_names.map do |name|
         | 
| 114 | 
            +
                  all_nodes.select { |node| node.name == name }.reduce(&:+).nodes[0]
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
              end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
              # @return [Array<Node>]
         | 
| 119 | 
            +
              def merge_nodes_with(other)
         | 
| 120 | 
            +
                if other.is_a? Node
         | 
| 121 | 
            +
                  if self.nodes.map(&:name).include?(other.name)
         | 
| 122 | 
            +
                    equal_names = lambda { |node| node.name == other.name }
         | 
| 123 | 
            +
                    collection_merged = self.nodes.select(&equal_names)[0] + other
         | 
| 124 | 
            +
                    other = collection_merged.nodes[0]
         | 
| 125 | 
            +
                  end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                  nodes_merged = self.nodes + [other]
         | 
| 128 | 
            +
                elsif other.is_a? NodesCollection
         | 
| 129 | 
            +
                  self_dedicated_nodes = self.nodes_not_in other
         | 
| 130 | 
            +
                  other_dedicated_nodes = other.nodes_not_in self
         | 
| 131 | 
            +
                  common_nodes = self.merge_common_nodes_with other
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                  nodes_merged = self_dedicated_nodes + common_nodes + other_dedicated_nodes
         | 
| 134 | 
            +
                end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                nodes_merged
         | 
| 137 | 
            +
              end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
              def files
         | 
| 140 | 
            +
                nodes.select(&:file?)
         | 
| 141 | 
            +
              end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
              def dirs
         | 
| 144 | 
            +
                nodes.select(&:dir?)
         | 
| 145 | 
            +
              end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
              def sort!
         | 
| 148 | 
            +
                nodes.sort!
         | 
| 149 | 
            +
              end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
              def to_primitive
         | 
| 152 | 
            +
                nodes.map(&:to_primitive)
         | 
| 153 | 
            +
              end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
              def valid?
         | 
| 156 | 
            +
                nodes &&
         | 
| 157 | 
            +
                    nodes.is_a?(Array) &&
         | 
| 158 | 
            +
                    nodes.all? { |node| node.is_a?(Node) } &&
         | 
| 159 | 
            +
                    nodes.all?(&:valid?)
         | 
| 160 | 
            +
                #TODO compare uniqueness of file and dir names.
         | 
| 161 | 
            +
              end
         | 
| 162 | 
            +
             | 
| 163 | 
            +
              def to_tree_s(depth = 0, open_parents = [])
         | 
| 164 | 
            +
                tree_s = ''
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                if nodes.length > 1
         | 
| 167 | 
            +
                  to_tree_s = lambda { |node| node.to_tree_s(depth, open_parents, false) }
         | 
| 168 | 
            +
                  tree_s << nodes[0..-2].map(&to_tree_s) * ''
         | 
| 169 | 
            +
                end
         | 
| 170 | 
            +
                tree_s << nodes.last.to_tree_s(depth, open_parents)
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                tree_s
         | 
| 173 | 
            +
              end
         | 
| 174 | 
            +
             | 
| 175 | 
            +
              private
         | 
| 176 | 
            +
              def add_node(other)
         | 
| 177 | 
            +
              end
         | 
| 178 | 
            +
             | 
| 179 | 
            +
            end
         | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require File.join File.dirname(__FILE__), '../lib/node'
         | 
| 4 | 
            +
            require File.join File.dirname(__FILE__), '../lib/nodes_collection'
         | 
| 5 | 
            +
            require File.join File.dirname(__FILE__), '../lib/bash_color'
         | 
| 6 | 
            +
            require File.join File.dirname(__FILE__), '../src/git_status_tree'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            # GIT STATUS
         | 
| 9 | 
            +
            #   ' '  unmodified
         | 
| 10 | 
            +
            #   'M'  modified
         | 
| 11 | 
            +
            #   'A'  added
         | 
| 12 | 
            +
            #   'D'  deleted
         | 
| 13 | 
            +
            #   'R'  renamed
         | 
| 14 | 
            +
            #   'C'  copied
         | 
| 15 | 
            +
            #   'U'  updated but unmerged
         | 
| 16 | 
            +
            #   '??' new
         | 
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            # GitStatusTree
         | 
| 2 | 
            +
            # use GitStatusTree.new.to_s to print the current git-status-tree
         | 
| 3 | 
            +
            class GitStatusTree
         | 
| 4 | 
            +
              attr_reader :files, :nodes, :tree
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              def initialize(options = {})
         | 
| 7 | 
            +
                config = %x(git config --global status-tree.indent).strip
         | 
| 8 | 
            +
                config = (config =~ /\A\d+\z/) ? config.to_i : nil
         | 
| 9 | 
            +
                indent = options[:indent] || config || 4
         | 
| 10 | 
            +
                indent = 2 if indent < 2
         | 
| 11 | 
            +
                indent = 10 if indent > 10
         | 
| 12 | 
            +
                Node.indent = indent
         | 
| 13 | 
            +
                @files = (%x(git status --porcelain)).split(/\n/)
         | 
| 14 | 
            +
                @nodes = files.map{|file| Node.create_from_string file}
         | 
| 15 | 
            +
                @tree = nodes.reduce{|a, i| (a+i).nodes[0]}
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              def to_s
         | 
| 19 | 
            +
                if tree.nil?
         | 
| 20 | 
            +
                  '(working directory clean)'
         | 
| 21 | 
            +
                else
         | 
| 22 | 
            +
                  tree.to_tree_s
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: git-status-tree
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 1.0.0
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Wolfgang Teuber
         | 
| 8 | 
            +
            autorequire:
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2021-09-28 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies: []
         | 
| 13 | 
            +
            description: git-status-tree is a command line tool that shows git repository changes
         | 
| 14 | 
            +
              in a file tree.
         | 
| 15 | 
            +
            email: knugie@gmx.net
         | 
| 16 | 
            +
            executables:
         | 
| 17 | 
            +
            - git-status-tree
         | 
| 18 | 
            +
            extensions:
         | 
| 19 | 
            +
            - ext/git_tree/extconf.rb
         | 
| 20 | 
            +
            extra_rdoc_files: []
         | 
| 21 | 
            +
            files:
         | 
| 22 | 
            +
            - bin/git-status-tree
         | 
| 23 | 
            +
            - bin/git_add_alias_tree
         | 
| 24 | 
            +
            - bin/git_remove_alias_tree
         | 
| 25 | 
            +
            - ext/git_tree/Makefile
         | 
| 26 | 
            +
            - ext/git_tree/extconf.rb
         | 
| 27 | 
            +
            - lib/bash_color.rb
         | 
| 28 | 
            +
            - lib/node.rb
         | 
| 29 | 
            +
            - lib/nodes_collection.rb
         | 
| 30 | 
            +
            - lib/rubygems_plugin.rb
         | 
| 31 | 
            +
            - src/git-status-tree.rb
         | 
| 32 | 
            +
            - src/git_status_tree.rb
         | 
| 33 | 
            +
            homepage: https://github.com/knugie/git-status-tree
         | 
| 34 | 
            +
            licenses:
         | 
| 35 | 
            +
            - MIT
         | 
| 36 | 
            +
            - GPL-2.0
         | 
| 37 | 
            +
            metadata:
         | 
| 38 | 
            +
              source_code_uri: https://github.com/knugie/git-status-tree
         | 
| 39 | 
            +
            post_install_message:
         | 
| 40 | 
            +
            rdoc_options: []
         | 
| 41 | 
            +
            require_paths:
         | 
| 42 | 
            +
            - lib
         | 
| 43 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 44 | 
            +
              requirements:
         | 
| 45 | 
            +
              - - ">="
         | 
| 46 | 
            +
                - !ruby/object:Gem::Version
         | 
| 47 | 
            +
                  version: '0'
         | 
| 48 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 49 | 
            +
              requirements:
         | 
| 50 | 
            +
              - - ">="
         | 
| 51 | 
            +
                - !ruby/object:Gem::Version
         | 
| 52 | 
            +
                  version: '0'
         | 
| 53 | 
            +
            requirements: []
         | 
| 54 | 
            +
            rubygems_version: 3.2.3
         | 
| 55 | 
            +
            signing_key:
         | 
| 56 | 
            +
            specification_version: 4
         | 
| 57 | 
            +
            summary: git status in file tree format
         | 
| 58 | 
            +
            test_files: []
         |