git-status-tree 1.0.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee588f3827fbbc7ce9a9d78bc8b4a3452d17bfc5a9426abe4246c617f52e9ee3
4
- data.tar.gz: 45bff253438b38d1464566fe9540b5d91f75505bee41575e4f26b106b4a48c47
3
+ metadata.gz: 0f6f3fdcc24a2e1769b654a63cd1e4aa40b0610b83e094993fbfe24b75f01b06
4
+ data.tar.gz: 3024ed0e9d6281faba5fc1e8f2998198a6484ad9af25bace6e25c54167a73951
5
5
  SHA512:
6
- metadata.gz: f09d66715e49aff087232e2b1b770935a595647753d5f8dcdfa72813b854885661fd25f89e47b201802dbf75dfb06b95de0a15f184847605b12717b4b202b606
7
- data.tar.gz: b45b6f3aa2884014934795361670401b1b98027ae98076049c135f8267e57c4f139dfe574039f2d77cfabc067c770467514fb2ece6a95a09829fbd43fbdb54a3
6
+ metadata.gz: 8d74fb836bb2780566de95ca3063ddf8436582471dbcfbb97dfb93c6e15d373e38b523c45b0bcb6c9322c6428be36bca729b2fb32876acc3ef412bc3831bfa1c
7
+ data.tar.gz: 1f322e2c9d3f99ddfc91585798853ec4c0c5a8c54ac5903f729c765194ad512eb575f3f2a1715a001d6b3965fb1f05df5b49a712f910c4fd48dce7b37a90664e
data/bin/git-status-tree CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
- # encoding: utf-8
2
+ # frozen_string_literal: true
3
3
 
4
- require File.join File.dirname(__FILE__), '../src/git-status-tree'
4
+ require File.join File.dirname(__FILE__), '../src/git_status_tree'
5
5
 
6
6
  puts GitStatusTree.new.to_s
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'mkmf'
2
4
 
3
5
  find_executable('bash')
@@ -5,11 +7,11 @@ find_executable('git')
5
7
  find_executable('make')
6
8
 
7
9
  # 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
+ compile = File.join(Dir.pwd, "git_tree.#{RbConfig::CONFIG['DLEXT']}")
11
+ File.open(compile, 'w') {}
10
12
 
11
13
  # Install "git tree"
12
14
  puts `../../bin/git_add_alias_tree`
13
15
 
14
16
  # Trick Rubygems into thinking the Makefile was executed
15
- $makefile_created = true
17
+ $makefile_created = true # rubocop:disable Style/GlobalVars
data/lib/bash_color.rb CHANGED
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module BashColor
4
4
  NONE = "\033[0m"
data/lib/node.rb CHANGED
@@ -1,208 +1,223 @@
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
1
+ # frozen_string_literal: true
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 # rubocop:disable Metrics/ClassLength
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
+ validate_name!(name)
18
+
19
+ msg = '"children" must be a NodesCollection or nil.'
20
+ valid_nodes_collection = children.nil? || children.is_a?(NodesCollection)
21
+ raise NodeChildrenError, msg unless valid_nodes_collection
22
+
23
+ @name = name
24
+ @children = children
25
+ @status = status || '??'
26
+ end
27
+
28
+ def self.create_from_string(gs_porcelain)
29
+ msg = '"str_node" must be String.'
30
+ raise NodeTypeError, msg unless gs_porcelain.is_a? String
31
+ raise NodeNameError, '"str_node" too short.' if gs_porcelain.length < 4
32
+
33
+ node_from_gs(gs_porcelain)
34
+ end
35
+
36
+ def self.instances?
37
+ ->(node) { node.is_a?(Node) }
38
+ end
39
+
40
+ def self.node_from_gs(gs_porcelain)
41
+ status = status(gs_porcelain)
42
+ ary_nodes = "./#{gs_porcelain[3..]}".split(%r{/})
43
+
44
+ name = ary_nodes.shift
45
+ if ary_nodes.any?
46
+ children = NodesCollection.create_from_array(ary_nodes, status)
47
+ new(name, children)
48
+ else
49
+ new(name, nil, status)
50
+ end
51
+ end
52
+
53
+ private_class_method :node_from_gs
54
+
55
+ def self.status(gs_porcelain)
56
+ status = "#{gs_porcelain[0]}+" if gs_porcelain[1] == ' '
57
+ status = gs_porcelain[1] unless gs_porcelain[1] == ' '
58
+ status
59
+ end
60
+ private_class_method :status
61
+
62
+ def to_primitive
63
+ if dir?
64
+ { name => children.to_primitive }
65
+ else
66
+ name
67
+ end
68
+ end
69
+
70
+ def file?
71
+ children.nil?
72
+ end
73
+
74
+ def dir?
75
+ !file?
76
+ end
77
+
78
+ def valid?
79
+ return valid_dir? if dir?
80
+ return valid_file? if file?
81
+
82
+ false
83
+ end
84
+
85
+ # @return [NodesCollection]
86
+ def +(other)
87
+ raise 'not valid' unless valid? && other.valid?
88
+ raise "not a #{self.class}" unless other.is_a?(self.class)
89
+
90
+ tmp_children = [children, other.children].compact.inject(&:+)
91
+
92
+ NodesCollection.new([self.class.new(name, tmp_children)])
93
+ end
94
+
95
+ def <=>(other)
96
+ return (name <=> other.name) if file? == other.file?
97
+ return -1 if dir? && other.file?
98
+ return 1 if file? && other.dir?
99
+
100
+ 0
101
+ end
102
+
103
+ def to_tree_s(depth = 0, open_parents = [0], last: true)
104
+ open_parents << depth
105
+
106
+ pre = pre_tree(depth, open_parents, last)
107
+
108
+ str_tree = "#{pre}#{color_name}\n"
109
+ str_tree += children.to_tree_s(depth + 1, open_parents) if children
110
+
111
+ str_tree
112
+ end
113
+
114
+ # 'M' modified
115
+ def modified?
116
+ status.include?('M')
117
+ end
118
+
119
+ # 'A' added
120
+ def added?
121
+ status.include?('A')
122
+ end
123
+
124
+ # 'D' deleted
125
+ def deleted?
126
+ status.include?('D')
127
+ end
128
+
129
+ # 'R' renamed
130
+ def renamed?
131
+ status.include?('R')
132
+ end
133
+
134
+ # 'C' copied
135
+ def copied?
136
+ status.include?('C')
137
+ end
138
+
139
+ # 'U' updated but unmerged
140
+ def unmerged?
141
+ status.include?('U')
142
+ end
143
+
144
+ # '?' new
145
+ def new?
146
+ status.include?('?')
147
+ end
148
+
149
+ # '+' staged
150
+ def staged?
151
+ status.include?('+')
152
+ end
153
+
154
+ private
155
+
156
+ def validate_name!(name)
157
+ msg = '"name" must be a String.'
158
+ raise NodeNameError, msg unless name.is_a? String
159
+
160
+ msg = '"name" must have at least one character.'
161
+ raise NodeNameError, msg if name.empty?
162
+
163
+ msg = '"name" must not contain "/", use create_from_string.'
164
+ raise NodeNameError, msg if name =~ %r{/}
165
+ end
166
+
167
+ def color_name
168
+ color_name = ''
169
+ if dir?
170
+ color_name += BashColor::EMB + name
171
+ else
172
+ color_name += BashColor::G if staged?
173
+ color_name += BashColor::R unless staged?
174
+ color_name += "#{name} (#{status})"
175
+ end
176
+ color_name + BashColor::NONE
177
+ end
178
+
179
+ # Has a valid name?
180
+ def name_valid?
181
+ name.is_a?(String) &&
182
+ name.length.positive? &&
183
+ name.match(%r{/}).nil?
184
+ end
185
+
186
+ # Is a valid dir?
187
+ def valid_dir?
188
+ name_valid? &&
189
+ children.is_a?(NodesCollection) &&
190
+ children.valid?
191
+ end
192
+
193
+ # Is a valid file?
194
+ def valid_file?
195
+ name_valid? &&
196
+ children.nil?
197
+ end
198
+
199
+ def pre_tree(depth, open_parents, last)
200
+ if depth.zero?
201
+ ''
202
+ elsif depth.positive?
203
+ pre_ary = Array.new(depth).fill(' ')
204
+ indent = self.class.indent - 2
205
+
206
+ open_parents.each { |idx| pre_ary[idx] = "│#{' ' * indent} " if pre_ary[idx] == ' ' }
207
+
208
+ sibling(depth, indent, last, open_parents, pre_ary)
209
+
210
+ pre_ary * ''
211
+ end
212
+ end
213
+
214
+ def sibling(depth, indent, last, open_parents, pre_ary)
215
+ if last
216
+ pre_ary[-1] = '└'
217
+ open_parents.delete(depth - 1)
218
+ else
219
+ pre_ary[-1] = '├'
220
+ end
221
+ pre_ary[-1] += "#{'─' * indent} "
222
+ end
223
+ end
@@ -1,179 +1,194 @@
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
1
+ # frozen_string_literal: true
2
+
3
+ # error class for invalid NodeCollection types
4
+ class NodesCollectionTypeError < StandardError
5
+ end
6
+
7
+ # collection of nodes
8
+ class NodesCollection # rubocop:disable Metrics/ClassLength
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(%r{/})
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 = ->(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(all_nodes)
30
+ raise msg unless all_nodes.all?(&Node.instances?)
31
+
32
+ grouped_nodes = all_nodes.group_by(&:name)
33
+ plain_nodes = plain_nodes(grouped_nodes)
34
+ merged_nodes = merged_nodes(grouped_nodes)
35
+
36
+ new(plain_nodes + merged_nodes)
37
+ end
38
+
39
+ def self.create_from_valid_array(ary_nodes, status)
40
+ name = ary_nodes.shift
41
+ node = if ary_nodes.any?
42
+ children = create_from_valid_array(ary_nodes, status)
43
+ Node.new(name, children)
44
+ else
45
+ Node.new(name, nil, status)
46
+ end
47
+ new([node])
48
+ end
49
+
50
+ private_class_method :create_from_valid_array
51
+
52
+ def self.merged_nodes(grouped_nodes)
53
+ merged_nodes = []
54
+ grouped_nodes.each do |_, nodes|
55
+ merged_nodes << (nodes[0] + nodes[1]).nodes[0] if nodes.length == 2
56
+ end
57
+ merged_nodes
58
+ end
59
+ private_class_method :merged_nodes
60
+
61
+ def self.plain_nodes(grouped_nodes)
62
+ grouped_nodes.filter { |_, nodes| nodes.length == 1 }.values.flatten(1)
63
+ end
64
+ private_class_method :plain_nodes
65
+
66
+ def initialize(nodes = [])
67
+ nodes = [nodes].flatten(1)
68
+
69
+ msg = '"nodes" must only contain Nodes.'
70
+ are_nodes = ->(node) { node.is_a?(Node) }
71
+ raise NodesCollectionTypeError, msg unless nodes.all?(&are_nodes)
72
+
73
+ @nodes = nodes
74
+ end
75
+
76
+ # @return [NodesCollection]
77
+ def +(other)
78
+ raise 'not a Node or NodesCollection' unless other.is_a?(Node) || other.is_a?(self.class)
79
+
80
+ all_nodes = merge_nodes_with other
81
+
82
+ dir_nodes = dir_nodes(all_nodes)
83
+ file_nodes = file_nodes(all_nodes)
84
+
85
+ self.class.new(dir_nodes + file_nodes)
86
+ end
87
+
88
+ # @return [Integer]
89
+ def <=>(other)
90
+ to_primitive <=> other.to_primitive
91
+ end
92
+
93
+ # @return [Array<Node>]
94
+ def nodes_not_in(other)
95
+ self_names = nodes.map(&:name)
96
+ other_names = other.nodes.map(&:name)
97
+
98
+ self_only_names = self_names - (self_names & other_names)
99
+
100
+ nodes.select { |node| self_only_names.include?(node.name) }
101
+ end
102
+
103
+ # @return [Array<Node>]
104
+ def merge_common_nodes_with(other)
105
+ self_names = nodes.map(&:name)
106
+ other_names = other.nodes.map(&:name)
107
+
108
+ common_names = self_names & other_names
109
+ all_nodes = nodes + other.nodes
110
+
111
+ common_names.map do |name|
112
+ all_nodes.select { |node| node.name == name }.reduce(&:+).nodes[0]
113
+ end
114
+ end
115
+
116
+ # @return [Array<Node>]
117
+ def merge_nodes_with(other)
118
+ case other
119
+ when Node
120
+ nodes_merged = merge_nodes_with_node(other)
121
+ when NodesCollection
122
+ nodes_merged = merge_nodes_with_collection(other)
123
+ end
124
+
125
+ nodes_merged
126
+ end
127
+
128
+ def files
129
+ nodes.select(&:file?)
130
+ end
131
+
132
+ def dirs
133
+ nodes.select(&:dir?)
134
+ end
135
+
136
+ def sort!
137
+ nodes.sort!
138
+ end
139
+
140
+ def to_primitive
141
+ nodes.map(&:to_primitive)
142
+ end
143
+
144
+ def valid?
145
+ nodes.is_a?(Array) &&
146
+ nodes.all? { |node| node.is_a?(Node) } &&
147
+ nodes&.all?(&:valid?)
148
+ # TODO: compare uniqueness of file and dir names.
149
+ end
150
+
151
+ def to_tree_s(depth = 0, open_parents = [])
152
+ tree_s = ''
153
+
154
+ if nodes.length > 1
155
+ to_tree_s = ->(node) { node.to_tree_s(depth, open_parents, last: false) }
156
+ tree_s += nodes[0..-2].map(&to_tree_s) * ''
157
+ end
158
+ tree_s += nodes.last.to_tree_s(depth, open_parents)
159
+
160
+ tree_s
161
+ end
162
+
163
+ def file_nodes(all_nodes)
164
+ all_files = all_nodes.select(&:file?)
165
+ files_collection = self.class.new all_files
166
+ files_collection.sort!
167
+ end
168
+
169
+ def dir_nodes(all_nodes)
170
+ all_dirs = all_nodes.select(&:dir?)
171
+ dirs_collection = self.class.new_from_nodes_array all_dirs
172
+ dirs_collection.sort!
173
+ end
174
+
175
+ def merge_nodes_with_collection(other)
176
+ self_dedicated_nodes = nodes_not_in other
177
+ other_dedicated_nodes = other.nodes_not_in self
178
+ common_nodes = merge_common_nodes_with other
179
+
180
+ self_dedicated_nodes + common_nodes + other_dedicated_nodes
181
+ end
182
+
183
+ def merge_nodes_with_node(other)
184
+ if nodes.map(&:name).include?(other.name)
185
+ equal_names = ->(node) { node.name == other.name }
186
+ collection_merged = nodes.select(&equal_names)[0] + other
187
+ other = collection_merged.nodes[0]
188
+ end
189
+
190
+ nodes + [other]
191
+ end
192
+
193
+ def add_node(other); end
194
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Gem.pre_uninstall do |uninstaller|
2
4
  bin_dir = uninstaller.spec.bin_dir
3
5
  git_remove_alias_tree = File.join(bin_dir, 'git_remove_alias_tree')
@@ -1,18 +1,20 @@
1
+ # frozen_string_literal: true
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
+
1
8
  # GitStatusTree
2
9
  # use GitStatusTree.new.to_s to print the current git-status-tree
3
10
  class GitStatusTree
4
11
  attr_reader :files, :nodes, :tree
5
12
 
6
13
  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]}
14
+ Node.indent = indent(options)
15
+ @files = `git status --porcelain`.split(/\n/)
16
+ @nodes = files.map { |file| Node.create_from_string file }
17
+ @tree = nodes.reduce { |a, i| (a + i).nodes[0] }
16
18
  end
17
19
 
18
20
  def to_s
@@ -22,4 +24,18 @@ class GitStatusTree
22
24
  tree.to_tree_s
23
25
  end
24
26
  end
27
+
28
+ private
29
+
30
+ def indent(options)
31
+ indent = options[:indent] || config || 4
32
+ indent = 2 if indent < 2
33
+ indent = 10 if indent > 10
34
+ indent
35
+ end
36
+
37
+ def config
38
+ config = `git config --global status-tree.indent`.strip
39
+ config =~ /\A\d+\z/ ? config.to_i : nil
40
+ end
25
41
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-status-tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wolfgang Teuber
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-07 00:00:00.000000000 Z
11
+ date: 2023-02-06 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: git-status-tree is a command line tool that shows git repository changes
14
14
  in a file tree.
@@ -28,7 +28,6 @@ files:
28
28
  - lib/node.rb
29
29
  - lib/nodes_collection.rb
30
30
  - lib/rubygems_plugin.rb
31
- - src/git-status-tree.rb
32
31
  - src/git_status_tree.rb
33
32
  homepage: https://github.com/knugie/git-status-tree
34
33
  licenses:
@@ -44,14 +43,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
44
43
  requirements:
45
44
  - - ">="
46
45
  - !ruby/object:Gem::Version
47
- version: '0'
46
+ version: '2.7'
48
47
  required_rubygems_version: !ruby/object:Gem::Requirement
49
48
  requirements:
50
49
  - - ">="
51
50
  - !ruby/object:Gem::Version
52
51
  version: '0'
53
52
  requirements: []
54
- rubygems_version: 3.2.3
53
+ rubygems_version: 3.1.6
55
54
  signing_key:
56
55
  specification_version: 4
57
56
  summary: git status in file tree format
@@ -1,16 +0,0 @@
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
- # '??' other