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: []
|