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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2eaa4e24f5f6fed7c2340c5a4ae93e1cf51247be86881d043688755dc4796e76
4
- data.tar.gz: 6b7916c3a0d9ca337884e336c3eb47a021bde591fc2287bf08048bd3e61855a5
3
+ metadata.gz: 1b5f775e1ebd97220c6705c47a171091da213c9959c925ebef136ff5a5a29733
4
+ data.tar.gz: 6c2d2dd1e7549110a29ce0abdd599b9c87a64aec3428a92ef06f927fa9e1a21c
5
5
  SHA512:
6
- metadata.gz: b3bac80ae5b85406a6ba49dd584af3c7afa92ccbfa3c29b38206f00d7852aa3df60efe2e3ff8291d73af62a64bf8eaa0692f2a5cc8c91eb96317cd4902cd0e02
7
- data.tar.gz: ebf4bfae6b713928c5fb6ef8b3d61459ee27398701346a13546dad8f62222c42b791c3ff22ae187a6b7865fba734ac7e3e0437ead298e43c96bc3fed531e4ad5
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)
@@ -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 "!sh -c \"$BIN_DIR/git-status-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
- str_tree = "#{pre}#{color_name}\n"
160
- str_tree += children.to_tree_s(depth + 1, open_parents) if children
161
-
162
- str_tree
163
- end
164
-
165
- def modified?
166
- status.include?('M')
167
- end
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 staged?
194
- status.include?('+')
195
- end
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
- open_parents.each { |idx| pre_ary[idx] = "│#{' ' * indent} " if pre_ary[idx] == ' ' }
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
- sibling(depth, indent, last, open_parents, pre_ary)
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
- pre_ary * ''
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
@@ -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.1.0
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-06-25 00:00:00.000000000 Z
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