git-status-tree 3.1.0 → 3.3.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 +4 -4
- data/VERSION +1 -0
- data/bin/git-status-tree +11 -1
- data/bin/git_add_alias_tree +1 -1
- data/lib/node.rb +39 -50
- data/lib/node_collapsing.rb +110 -0
- data/src/git_status_tree.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1b5f775e1ebd97220c6705c47a171091da213c9959c925ebef136ff5a5a29733
|
4
|
+
data.tar.gz: 6c2d2dd1e7549110a29ce0abdd599b9c87a64aec3428a92ef06f927fa9e1a21c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 063414646c8c96d17705a7db7cc380797bda2116e24e045876b14020ad854418cffcd6ddaf4a320ef7992245e8d4bb7de1b00632a51d557f0b76235881e305bd
|
7
|
+
data.tar.gz: 7f3fcc689ce71d0221a2e0e640253c272437c2ab94210f5e6c6af4f45752538cd310b1fc11605f77f04f4f3fa0cfb8b381ac1478099f28dca3fc2124f50210ae
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.3.0
|
data/bin/git-status-tree
CHANGED
@@ -5,9 +5,19 @@ require 'optparse'
|
|
5
5
|
require File.join File.dirname(__FILE__), '../src/git_status_tree'
|
6
6
|
require File.join File.dirname(__FILE__), '../lib/version'
|
7
7
|
|
8
|
+
options = {}
|
9
|
+
|
8
10
|
parser = OptionParser.new do |opts|
|
9
11
|
opts.banner = 'Usage: git-status-tree [options]'
|
10
12
|
|
13
|
+
opts.on('-i', '--indent INDENT', Integer, 'Set indentation (2-10 spaces)') do |indent|
|
14
|
+
options[:indent] = indent
|
15
|
+
end
|
16
|
+
|
17
|
+
opts.on('-c', '--collapse', 'Collapse directories containing only another directory') do
|
18
|
+
options[:collapse] = true
|
19
|
+
end
|
20
|
+
|
11
21
|
opts.on('-v', '--version', 'Show version') do
|
12
22
|
puts "git-status-tree #{GitStatusTree::VERSION}"
|
13
23
|
exit 0
|
@@ -27,4 +37,4 @@ rescue OptionParser::InvalidOption => e
|
|
27
37
|
exit 1
|
28
38
|
end
|
29
39
|
|
30
|
-
puts GitStatusTree.new
|
40
|
+
puts GitStatusTree.new(options)
|
data/bin/git_add_alias_tree
CHANGED
@@ -5,7 +5,7 @@ BIN_DIR=`pwd`
|
|
5
5
|
popd > /dev/null
|
6
6
|
|
7
7
|
git config --global status-tree.indent 4
|
8
|
-
git config --global alias.tree "!
|
8
|
+
git config --global alias.tree "!exec $BIN_DIR/git-status-tree"
|
9
9
|
echo '#############################'
|
10
10
|
echo '# "git tree" has been added #'
|
11
11
|
echo '# Run: git tree #'
|
data/lib/node.rb
CHANGED
@@ -1,19 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'node_collapsing'
|
4
|
+
|
3
5
|
class NodeNameError < StandardError; end
|
4
6
|
class NodeChildrenError < StandardError; end
|
5
7
|
class NodeTypeError < StandardError; end
|
6
8
|
|
7
9
|
# A Node represents a file or directory in the git-status-tree
|
8
10
|
class Node
|
11
|
+
include NodeCollapsing
|
12
|
+
|
9
13
|
class << self
|
10
|
-
attr_accessor :indent
|
14
|
+
attr_accessor :indent, :collapse_dirs
|
11
15
|
end
|
12
16
|
|
13
17
|
attr_accessor :status, :name, :children
|
14
18
|
|
15
19
|
def initialize(name, children = nil, status = nil)
|
16
20
|
self.class.indent ||= 4
|
21
|
+
self.class.collapse_dirs ||= false
|
17
22
|
validate_name!(name)
|
18
23
|
|
19
24
|
msg = '"children" must be a NodesCollection or nil.'
|
@@ -124,13 +129,9 @@ class Node
|
|
124
129
|
end
|
125
130
|
end
|
126
131
|
|
127
|
-
def file?
|
128
|
-
children.nil?
|
129
|
-
end
|
132
|
+
def file? = children.nil?
|
130
133
|
|
131
|
-
def dir?
|
132
|
-
!file?
|
133
|
-
end
|
134
|
+
def dir? = !file?
|
134
135
|
|
135
136
|
def valid?
|
136
137
|
file? ? valid_file? : valid_dir?
|
@@ -156,43 +157,25 @@ class Node
|
|
156
157
|
|
157
158
|
pre = pre_tree(depth, open_parents, last)
|
158
159
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
def added?
|
170
|
-
status.include?('A')
|
171
|
-
end
|
172
|
-
|
173
|
-
def deleted?
|
174
|
-
status.include?('D')
|
175
|
-
end
|
176
|
-
|
177
|
-
def renamed?
|
178
|
-
status.include?('R')
|
179
|
-
end
|
180
|
-
|
181
|
-
def copied?
|
182
|
-
status.include?('C')
|
183
|
-
end
|
184
|
-
|
185
|
-
def unmerged?
|
186
|
-
status.include?('U')
|
187
|
-
end
|
188
|
-
|
189
|
-
def new?
|
190
|
-
status.include?('?')
|
160
|
+
# Handle directory collapsing if enabled
|
161
|
+
if self.class.collapse_dirs && collapsible?
|
162
|
+
render_collapsed_tree(pre, depth, open_parents)
|
163
|
+
else
|
164
|
+
# Normal rendering
|
165
|
+
str_tree = "#{pre}#{color_name}\n"
|
166
|
+
str_tree += children.to_tree_s(depth + 1, open_parents) if children
|
167
|
+
str_tree
|
168
|
+
end
|
191
169
|
end
|
192
170
|
|
193
|
-
def
|
194
|
-
|
195
|
-
|
171
|
+
def modified? = status.include?('M')
|
172
|
+
def added? = status.include?('A')
|
173
|
+
def deleted? = status.include?('D')
|
174
|
+
def renamed? = status.include?('R')
|
175
|
+
def copied? = status.include?('C')
|
176
|
+
def unmerged? = status.include?('U')
|
177
|
+
def new? = status.include?('?')
|
178
|
+
def staged? = status.include?('+')
|
196
179
|
|
197
180
|
private
|
198
181
|
|
@@ -238,18 +221,24 @@ class Node
|
|
238
221
|
end
|
239
222
|
|
240
223
|
def pre_tree(depth, open_parents, last)
|
241
|
-
if depth.zero?
|
242
|
-
|
243
|
-
elsif depth.positive?
|
244
|
-
pre_ary = Array.new(depth).fill(' ')
|
245
|
-
indent = self.class.indent - 2
|
224
|
+
return '' if depth.zero?
|
225
|
+
return '' unless depth.positive?
|
246
226
|
|
247
|
-
|
227
|
+
pre_ary = build_pre_array(depth, open_parents)
|
228
|
+
sibling(depth, self.class.indent - 2, last, open_parents, pre_ary)
|
229
|
+
pre_ary * ''
|
230
|
+
end
|
248
231
|
|
249
|
-
|
232
|
+
def build_pre_array(depth, open_parents)
|
233
|
+
spaces = ' ' * self.class.indent
|
234
|
+
pre_ary = Array.new(depth).fill(spaces)
|
235
|
+
indent = self.class.indent - 2
|
250
236
|
|
251
|
-
|
237
|
+
open_parents.each do |idx|
|
238
|
+
pre_ary[idx] = "│#{' ' * indent} " if pre_ary[idx] == spaces
|
252
239
|
end
|
240
|
+
|
241
|
+
pre_ary
|
253
242
|
end
|
254
243
|
|
255
244
|
def sibling(depth, indent, last, open_parents, pre_ary)
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Module for directory collapsing functionality
|
4
|
+
module NodeCollapsing
|
5
|
+
# Check if this directory can be collapsed (only contains one child, either file or directory)
|
6
|
+
def collapsible?
|
7
|
+
return false unless dir?
|
8
|
+
return false unless children && children.nodes.length == 1
|
9
|
+
return false if name == '.' # Never collapse the root node
|
10
|
+
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get the collapsed path for display
|
15
|
+
def collapsed_path
|
16
|
+
return name unless collapsible?
|
17
|
+
|
18
|
+
path_parts = [name]
|
19
|
+
build_collapsed_path_parts(path_parts)
|
20
|
+
path_parts.join('/')
|
21
|
+
end
|
22
|
+
|
23
|
+
# Get the deepest node in a collapsible chain
|
24
|
+
def deepest_collapsible_node
|
25
|
+
current = self
|
26
|
+
|
27
|
+
current = current.children.nodes.first while current.collapsible? && current.children.nodes.first.dir?
|
28
|
+
|
29
|
+
current
|
30
|
+
end
|
31
|
+
|
32
|
+
# Check if this is a collapsed path ending with a file
|
33
|
+
def collapsed_with_file?
|
34
|
+
return false unless collapsible?
|
35
|
+
|
36
|
+
deepest = deepest_collapsible_node
|
37
|
+
deepest.children && deepest.children.nodes.length == 1 && deepest.children.nodes.first.file?
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def build_collapsed_path_parts(path_parts)
|
43
|
+
current = self
|
44
|
+
|
45
|
+
# Traverse through collapsible directories
|
46
|
+
while current.collapsible? && current.children.nodes.first.dir?
|
47
|
+
child = current.children.nodes.first
|
48
|
+
path_parts << child.name
|
49
|
+
current = child
|
50
|
+
end
|
51
|
+
|
52
|
+
# Add file if the last child is a file
|
53
|
+
append_file_to_path(current, path_parts)
|
54
|
+
end
|
55
|
+
|
56
|
+
def append_file_to_path(node, path_parts)
|
57
|
+
return unless node.children && node.children.nodes.length == 1
|
58
|
+
|
59
|
+
child = node.children.nodes.first
|
60
|
+
path_parts << child.name if child.file?
|
61
|
+
end
|
62
|
+
|
63
|
+
def render_collapsed_tree(pre, depth, open_parents)
|
64
|
+
display_name = collapsed_path
|
65
|
+
|
66
|
+
if collapsed_with_file?
|
67
|
+
render_collapsed_file(pre, display_name)
|
68
|
+
else
|
69
|
+
render_collapsed_directory(pre, display_name, depth, open_parents)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def render_collapsed_file(pre, display_name)
|
74
|
+
deepest = deepest_collapsible_node
|
75
|
+
file_node = deepest.children.nodes.first
|
76
|
+
"#{pre}#{color_collapsed_file(display_name, file_node.status)}\n"
|
77
|
+
end
|
78
|
+
|
79
|
+
def render_collapsed_directory(pre, display_name, depth, open_parents)
|
80
|
+
str_tree = "#{pre}#{color_collapsed_name(display_name)}\n"
|
81
|
+
deepest = deepest_collapsible_node
|
82
|
+
# Only render children if they exist and have nodes
|
83
|
+
str_tree += deepest.children.to_tree_s(depth + 1, open_parents) if deepest.children&.nodes&.any?
|
84
|
+
str_tree
|
85
|
+
end
|
86
|
+
|
87
|
+
def color_collapsed_name(display_name)
|
88
|
+
BashColor::EMB + display_name + BashColor::NONE
|
89
|
+
end
|
90
|
+
|
91
|
+
def color_collapsed_file(display_path, status)
|
92
|
+
# Split the path to separate directories from the file
|
93
|
+
parts = display_path.split('/')
|
94
|
+
file_name = parts.pop
|
95
|
+
dir_path = parts.join('/')
|
96
|
+
|
97
|
+
if dir_path.empty?
|
98
|
+
# No directories, just the file
|
99
|
+
color_file_with_status(file_name, status)
|
100
|
+
else
|
101
|
+
# Directories in blue, file colored by status
|
102
|
+
"#{BashColor::EMB}#{dir_path}/#{BashColor::NONE}#{color_file_with_status(file_name, status)}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def color_file_with_status(file_name, status)
|
107
|
+
color = status.include?('+') ? BashColor::G : BashColor::R
|
108
|
+
"#{color}#{file_name} (#{status})#{BashColor::NONE}"
|
109
|
+
end
|
110
|
+
end
|
data/src/git_status_tree.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require File.join File.dirname(__FILE__), '../lib/node'
|
4
|
+
require File.join File.dirname(__FILE__), '../lib/node_collapsing'
|
4
5
|
require File.join File.dirname(__FILE__), '../lib/nodes_collection'
|
5
6
|
require File.join File.dirname(__FILE__), '../lib/bash_color'
|
6
7
|
require File.join File.dirname(__FILE__), '../lib/version'
|
@@ -11,6 +12,7 @@ class GitStatusTree
|
|
11
12
|
|
12
13
|
def initialize(options = {})
|
13
14
|
Node.indent = indent(options)
|
15
|
+
Node.collapse_dirs = options[:collapse] || false
|
14
16
|
@files = `git status --porcelain`.split("\n")
|
15
17
|
@nodes = files.map { |file| Node.create_from_string file }
|
16
18
|
@tree = nodes.reduce { |a, i| (a + i).nodes[0] }
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git-status-tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wolfgang Teuber
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-07-03 00:00:00.000000000 Z
|
11
11
|
dependencies: []
|
12
12
|
description: git-status-tree is a command line tool that shows git repository changes
|
13
13
|
in a file tree.
|
@@ -18,6 +18,7 @@ extensions:
|
|
18
18
|
- ext/git_tree/extconf.rb
|
19
19
|
extra_rdoc_files: []
|
20
20
|
files:
|
21
|
+
- VERSION
|
21
22
|
- bin/git-status-tree
|
22
23
|
- bin/git_add_alias_tree
|
23
24
|
- bin/git_remove_alias_tree
|
@@ -25,6 +26,7 @@ files:
|
|
25
26
|
- ext/git_tree/extconf.rb
|
26
27
|
- lib/bash_color.rb
|
27
28
|
- lib/node.rb
|
29
|
+
- lib/node_collapsing.rb
|
28
30
|
- lib/nodes_collection.rb
|
29
31
|
- lib/rubygems_plugin.rb
|
30
32
|
- lib/version.rb
|