git-crecord 1.0.8 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +8 -15
- data/Gemfile +2 -0
- data/README.md +2 -2
- data/Rakefile +6 -1
- data/bin/git-crecord +2 -0
- data/git-crecord.gemspec +9 -7
- data/lib/git_crecord/diff/difference.rb +18 -3
- data/lib/git_crecord/diff/file.rb +10 -5
- data/lib/git_crecord/diff/hunk.rb +8 -3
- data/lib/git_crecord/diff/line.rb +6 -2
- data/lib/git_crecord/diff.rb +14 -7
- data/lib/git_crecord/git.rb +25 -7
- data/lib/git_crecord/logger.rb +3 -1
- data/lib/git_crecord/quit_action.rb +1 -0
- data/lib/git_crecord/ui/color.rb +3 -1
- data/lib/git_crecord/ui/help_window.rb +4 -2
- data/lib/git_crecord/ui/hunks_window.rb +16 -9
- data/lib/git_crecord/ui.rb +4 -1
- data/lib/git_crecord/version.rb +3 -1
- data/lib/git_crecord.rb +24 -12
- data/test/git_crecord/diff/hunk_test.rb +3 -1
- data/test/system-test.sh +31 -11
- data/test/test_helper.rb +2 -0
- metadata +42 -16
- data/ext/mkrf_conf.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9faaad49aba11b3aad863f3923f1249cd6d83ae88148405bcb0799f6019cb6c5
|
4
|
+
data.tar.gz: 5ee14973f732a9117c37ea52cbc3040cdb4c4331a4342efb8624143121f84d87
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23c240bc80cda76c5aa13bd2d296b1a8f65a40a0364ea5eea0098f51b8af5f69a40f189a67abe8be19ac98f240e6bfd14f432aa051268e33352067305661c7c8
|
7
|
+
data.tar.gz: 8259cb00118d75e7ee7aa49c57ae949197fae66a6cc20e24a1644890fc4691a8049d88834dc91778d9689cd388566526337698e01884b9929314f5772353110d
|
data/.rubocop.yml
CHANGED
@@ -1,20 +1,13 @@
|
|
1
|
-
Metrics/LineLength:
|
2
|
-
Max: 80
|
3
|
-
|
4
|
-
Style/AlignParameters:
|
5
|
-
EnforcedStyle: with_fixed_indentation
|
6
|
-
|
7
|
-
Style/SpaceInsideHashLiteralBraces:
|
8
|
-
EnforcedStyle: no_space
|
9
|
-
|
10
|
-
Style/SpaceBeforeBlockBraces:
|
11
|
-
EnforcedStyle: no_space
|
12
|
-
|
13
|
-
Style/NumericLiterals:
|
14
|
-
MinDigits: 666
|
15
|
-
|
16
1
|
Documentation:
|
17
2
|
Enabled: false
|
18
3
|
|
4
|
+
Metrics/ClassLength:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
Metrics/BlockLength:
|
8
|
+
Max: 30
|
9
|
+
|
19
10
|
AllCops:
|
11
|
+
DisplayCopNames: true
|
20
12
|
DisplayStyleGuide: true
|
13
|
+
TargetRubyVersion: 2.3
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -15,6 +15,7 @@ $ gem install git-crecord
|
|
15
15
|
```shell
|
16
16
|
$ git crecord
|
17
17
|
$ git crecord --untracked-files # show untracked files
|
18
|
+
$ git crecord --reverse # unstage hunks
|
18
19
|
```
|
19
20
|
|
20
21
|
Key-bindings:
|
@@ -55,6 +56,5 @@ $ ln -s bin/git-crecord /usr/bin/git-crecord
|
|
55
56
|
|
56
57
|
Tests:
|
57
58
|
```shell
|
58
|
-
$ bundle exec rake
|
59
|
-
$ bundle exec rake systemtest
|
59
|
+
$ bundle exec rake
|
60
60
|
```
|
data/Rakefile
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rake'
|
2
4
|
require 'rake/testtask'
|
3
5
|
require 'bundler/gem_tasks'
|
6
|
+
require 'rubocop/rake_task'
|
4
7
|
|
5
8
|
Rake::TestTask.new do |t|
|
6
9
|
t.test_files = FileList['test/git_crecord/**/*test.rb']
|
@@ -11,4 +14,6 @@ task :systemtest do
|
|
11
14
|
sh(File.join(__dir__, 'test/system-test.sh'))
|
12
15
|
end
|
13
16
|
|
14
|
-
|
17
|
+
RuboCop::RakeTask.new
|
18
|
+
|
19
|
+
task default: %i[test systemtest rubocop]
|
data/bin/git-crecord
CHANGED
data/git-crecord.gemspec
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'lib/git_crecord/version'
|
2
4
|
|
3
5
|
GemSpec = Gem::Specification.new do |spec|
|
4
6
|
spec.required_rubygems_version = Gem::Requirement.new('>= 1.3.6')
|
5
7
|
spec.platform = Gem::Platform::RUBY
|
6
|
-
spec.required_ruby_version = '>= 2.
|
8
|
+
spec.required_ruby_version = '>= 2.3.0'
|
7
9
|
spec.name = 'git-crecord'
|
8
10
|
spec.version = GitCrecord::VERSION
|
9
11
|
spec.authors = 'Maik Brendler'
|
@@ -19,14 +21,14 @@ GemSpec = Gem::Specification.new do |spec|
|
|
19
21
|
'issue_tracker' => 'https://github.com/mbrendler/git-crecord/issues'
|
20
22
|
}
|
21
23
|
spec.require_paths = %w[lib]
|
22
|
-
spec.files = `git ls-files`.split($RS).delete_if
|
24
|
+
spec.files = `git ls-files`.split($RS).delete_if do |f|
|
25
|
+
%r{^(spec|test)/} =~ f
|
26
|
+
end
|
23
27
|
spec.test_files = `git ls-files`.split($RS).grep(%r{^(spec|test)/})
|
24
28
|
spec.executables = %w[git-crecord]
|
25
29
|
spec.has_rdoc = false
|
26
|
-
|
27
|
-
# ruby 2.0 includes curses and can't install curses-gem
|
28
|
-
# spec.add_dependency 'curses', '~> 1.0'
|
29
|
-
spec.extensions << 'ext/mkrf_conf.rb'
|
30
|
-
spec.add_development_dependency 'rake', '~> 10.1', '>= 10.1.1'
|
30
|
+
spec.add_dependency 'curses', '~>1.0'
|
31
31
|
spec.add_development_dependency 'minitest', '~> 5.8', '>= 5.8.4'
|
32
|
+
spec.add_development_dependency 'rake', '~> 10.1', '>= 10.1.1'
|
33
|
+
spec.add_development_dependency 'rubocop', '>= 0.56.0'
|
32
34
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../ui/color'
|
2
4
|
|
3
5
|
module GitCrecord
|
@@ -12,9 +14,18 @@ module GitCrecord
|
|
12
14
|
false => '[ ] ',
|
13
15
|
:partly => '[~] '
|
14
16
|
}.freeze
|
17
|
+
|
18
|
+
REVERSE_SELECTED_MAP = {
|
19
|
+
true => '[R] ',
|
20
|
+
false => '[X] ',
|
21
|
+
:partly => '[~] '
|
22
|
+
}.freeze
|
23
|
+
|
15
24
|
SELECTION_MARKER_WIDTH = SELECTED_MAP[true].size
|
16
25
|
|
17
|
-
def initialize
|
26
|
+
def initialize(reverse: false)
|
27
|
+
@reverse = reverse
|
28
|
+
@selection_marker_map = reverse ? REVERSE_SELECTED_MAP : SELECTED_MAP
|
18
29
|
@subs = []
|
19
30
|
end
|
20
31
|
|
@@ -44,15 +55,17 @@ module GitCrecord
|
|
44
55
|
def selected
|
45
56
|
s = selectable_subs.map(&:selected).uniq
|
46
57
|
return s[0] if s.size == 1
|
58
|
+
|
47
59
|
:partly
|
48
60
|
end
|
49
61
|
|
50
62
|
def selected=(value)
|
51
|
-
selectable_subs.each{ |sub| sub.selected = value }
|
63
|
+
selectable_subs.each { |sub| sub.selected = value }
|
52
64
|
end
|
53
65
|
|
54
66
|
def style(is_highlighted)
|
55
67
|
return Curses::A_BOLD | UI::Color.hl if is_highlighted
|
68
|
+
|
56
69
|
Curses::A_BOLD | UI::Color.normal
|
57
70
|
end
|
58
71
|
|
@@ -61,7 +74,9 @@ module GitCrecord
|
|
61
74
|
end
|
62
75
|
|
63
76
|
def prefix(line_number)
|
64
|
-
|
77
|
+
show_selection_marker = line_number.zero? && selectable?
|
78
|
+
return @selection_marker_map.fetch(selected) if show_selection_marker
|
79
|
+
|
65
80
|
' ' * SELECTION_MARKER_WIDTH
|
66
81
|
end
|
67
82
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'difference'
|
2
4
|
require_relative 'hunk'
|
3
5
|
require_relative '../ui/color'
|
@@ -8,28 +10,30 @@ module GitCrecord
|
|
8
10
|
attr_reader :filename_a
|
9
11
|
attr_reader :type
|
10
12
|
|
11
|
-
def initialize(filename_a, filename_b, type: :modified)
|
13
|
+
def initialize(filename_a, filename_b, type: :modified, reverse: false)
|
12
14
|
@filename_a = filename_a
|
13
15
|
@filename_b = filename_b
|
14
16
|
@type = type
|
15
17
|
@expanded = false
|
16
|
-
super()
|
18
|
+
super(reverse: reverse)
|
17
19
|
end
|
18
20
|
|
19
21
|
def to_s
|
20
|
-
prefix = {modified: 'M', untracked: '?'}.fetch(type)
|
22
|
+
prefix = { modified: 'M', untracked: '?' }.fetch(type)
|
21
23
|
return "#{prefix} #{@filename_a}" if @filename_a == @filename_b
|
24
|
+
|
22
25
|
"#{prefix} #{filename_a} -> #{filename_b}"
|
23
26
|
end
|
24
27
|
|
25
28
|
def info_string
|
26
|
-
line_count = subs.reduce(0){ |a, e| e.selectable_subs.size + a }
|
29
|
+
line_count = subs.reduce(0) { |a, e| e.selectable_subs.size + a }
|
27
30
|
" #{subs.size} hunk(s), #{line_count} line(s) changed"
|
28
31
|
end
|
29
32
|
|
30
33
|
def strings(width)
|
31
34
|
result = super
|
32
35
|
return result unless expanded
|
36
|
+
|
33
37
|
result += info_string.scan(/.{1,#{content_width(width)}}/)
|
34
38
|
result << ''
|
35
39
|
end
|
@@ -43,7 +47,7 @@ module GitCrecord
|
|
43
47
|
end
|
44
48
|
|
45
49
|
def <<(hunk)
|
46
|
-
subs << Hunk.new(hunk)
|
50
|
+
subs << Hunk.new(hunk, reverse: @reverse)
|
47
51
|
self
|
48
52
|
end
|
49
53
|
|
@@ -53,6 +57,7 @@ module GitCrecord
|
|
53
57
|
|
54
58
|
def generate_diff
|
55
59
|
return unless selected
|
60
|
+
|
56
61
|
[
|
57
62
|
"diff --git a/#{@filename_a} b/#{@filename_b}",
|
58
63
|
"--- a/#{@filename_a}",
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'difference'
|
2
4
|
require_relative 'line'
|
3
5
|
require_relative '../ui/color'
|
@@ -5,10 +7,10 @@ require_relative '../ui/color'
|
|
5
7
|
module GitCrecord
|
6
8
|
module Diff
|
7
9
|
class Hunk < Difference
|
8
|
-
def initialize(head)
|
10
|
+
def initialize(head, reverse: false)
|
9
11
|
@head = head
|
10
12
|
@expanded = true
|
11
|
-
super()
|
13
|
+
super(reverse: reverse)
|
12
14
|
end
|
13
15
|
|
14
16
|
def to_s
|
@@ -20,12 +22,13 @@ module GitCrecord
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def <<(line)
|
23
|
-
subs << Line.new(line)
|
25
|
+
subs << Line.new(line, reverse: @reverse)
|
24
26
|
self
|
25
27
|
end
|
26
28
|
|
27
29
|
def generate_diff
|
28
30
|
return nil unless selected
|
31
|
+
|
29
32
|
[generate_header, *subs.map(&:generate_diff).compact].join("\n")
|
30
33
|
end
|
31
34
|
|
@@ -33,6 +36,7 @@ module GitCrecord
|
|
33
36
|
old_start, old_count, new_start, new_count = parse_header
|
34
37
|
selectable_subs.each do |sub|
|
35
38
|
next if sub.selected
|
39
|
+
|
36
40
|
new_count -= 1 if sub.add?
|
37
41
|
new_count += 1 if sub.del?
|
38
42
|
end
|
@@ -42,6 +46,7 @@ module GitCrecord
|
|
42
46
|
def parse_header
|
43
47
|
match = @head.match(/@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@/)
|
44
48
|
raise "mismatching hunk-header - '#{@head}'" if match.nil?
|
49
|
+
|
45
50
|
[match[1], match[3] || 1, match[4], match[6] || 1].map(&:to_i)
|
46
51
|
end
|
47
52
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'difference'
|
2
4
|
require_relative '../ui/color'
|
3
5
|
|
@@ -40,10 +42,10 @@ module GitCrecord
|
|
40
42
|
class Line < Difference
|
41
43
|
attr_reader :selected
|
42
44
|
|
43
|
-
def initialize(line)
|
45
|
+
def initialize(line, reverse: false)
|
44
46
|
@line = line
|
45
47
|
@selected = true
|
46
|
-
super()
|
48
|
+
super(reverse: reverse)
|
47
49
|
end
|
48
50
|
|
49
51
|
def to_s
|
@@ -77,6 +79,7 @@ module GitCrecord
|
|
77
79
|
def generate_diff
|
78
80
|
return " #{@line[1..-1]}" if !selected && del?
|
79
81
|
return @line if selected
|
82
|
+
|
80
83
|
nil
|
81
84
|
end
|
82
85
|
|
@@ -84,6 +87,7 @@ module GitCrecord
|
|
84
87
|
return UI::Color.hl if is_highlighted
|
85
88
|
return UI::Color.green if add?
|
86
89
|
return UI::Color.red if del?
|
90
|
+
|
87
91
|
UI::Color.normal
|
88
92
|
end
|
89
93
|
end
|
data/lib/git_crecord/diff.rb
CHANGED
@@ -1,15 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'diff/file'
|
2
4
|
|
3
5
|
module GitCrecord
|
4
6
|
module Diff
|
5
|
-
def self.parse(diff)
|
7
|
+
def self.parse(diff, reverse = false)
|
6
8
|
files = []
|
7
9
|
enum = diff.lines.each
|
8
10
|
loop do
|
9
11
|
line = enum.next
|
10
12
|
line.chomp!
|
11
|
-
next files <<
|
13
|
+
next files << parse_file_head(line, enum, reverse) if file_start?(line)
|
12
14
|
next files[-1] << line if hunk_start?(line)
|
15
|
+
|
13
16
|
files[-1].add_hunk_line(line)
|
14
17
|
end
|
15
18
|
files
|
@@ -23,11 +26,14 @@ module GitCrecord
|
|
23
26
|
line.start_with?('@@')
|
24
27
|
end
|
25
28
|
|
26
|
-
def self.
|
27
|
-
enum.next # index ...
|
29
|
+
def self.parse_file_head(line, enum, reverse)
|
30
|
+
index_line = enum.next # index ... or new ...
|
31
|
+
is_new_file = index_line.start_with?('new')
|
32
|
+
enum.next if is_new_file
|
28
33
|
enum.next # --- ...
|
29
34
|
enum.next # +++ ...
|
30
|
-
|
35
|
+
type = is_new_file ? :untracked : :modified
|
36
|
+
File.new(*parse_filenames(line), type: type, reverse: reverse)
|
31
37
|
end
|
32
38
|
|
33
39
|
def self.parse_filenames(line)
|
@@ -35,7 +41,7 @@ module GitCrecord
|
|
35
41
|
end
|
36
42
|
|
37
43
|
def self.untracked_files(git_status)
|
38
|
-
git_status.lines.select{ |l| l.start_with?('??') }.flat_map do |path|
|
44
|
+
git_status.lines.select { |l| l.start_with?('??') }.flat_map do |path|
|
39
45
|
path = path.chomp[3..-1]
|
40
46
|
::File.directory?(path) ? untracked_dir(path) : untracked_file(path)
|
41
47
|
end.compact
|
@@ -46,7 +52,7 @@ module GitCrecord
|
|
46
52
|
lines, err = file_lines(filename)
|
47
53
|
file << "@@ -0,0 +1,#{lines.size} @@"
|
48
54
|
file.subs[0].subs << PseudoLine.new(err) if lines.empty?
|
49
|
-
lines.each{ |line| file.add_hunk_line("+#{line.chomp}") }
|
55
|
+
lines.each { |line| file.add_hunk_line("+#{line.chomp}") }
|
50
56
|
file.selected = false
|
51
57
|
end
|
52
58
|
end
|
@@ -64,6 +70,7 @@ module GitCrecord
|
|
64
70
|
def self.file_lines(filename)
|
65
71
|
encoding = file_encoding(filename)
|
66
72
|
return [[], 'binary'] if encoding == 'binary'
|
73
|
+
|
67
74
|
[::File.open(filename, "r:#{encoding}", &:readlines), nil]
|
68
75
|
end
|
69
76
|
end
|
data/lib/git_crecord/git.rb
CHANGED
@@ -1,17 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'logger'
|
2
4
|
require 'open3'
|
3
5
|
|
4
6
|
module GitCrecord
|
5
7
|
module Git
|
6
|
-
def self.stage(files)
|
8
|
+
def self.stage(files, reverse = false)
|
7
9
|
selected_files = files.select(&:selected)
|
8
|
-
|
10
|
+
untracked_files = selected_files.select { |file| file.type == :untracked }
|
11
|
+
add_files(untracked_files) unless reverse
|
9
12
|
diff = selected_files.map(&:generate_diff).join("\n")
|
10
|
-
_stage(diff).success?
|
13
|
+
status = _stage(diff, reverse).success?
|
14
|
+
return status unless reverse
|
15
|
+
|
16
|
+
reset_files(untracked_files.select { |file| file.selected == true })
|
17
|
+
true
|
11
18
|
end
|
12
19
|
|
13
|
-
def self._stage(diff)
|
14
|
-
cmd =
|
20
|
+
def self._stage(diff, reverse = false)
|
21
|
+
cmd = "git apply --cached --unidiff-zero #{reverse ? '-R' : ''} - "
|
15
22
|
content, status = Open3.capture2e(cmd, stdin_data: diff)
|
16
23
|
LOGGER.info(cmd)
|
17
24
|
LOGGER.info(diff)
|
@@ -33,6 +40,17 @@ module GitCrecord
|
|
33
40
|
system("git add -N #{filename}")
|
34
41
|
end
|
35
42
|
|
43
|
+
def self.reset_files(files)
|
44
|
+
files.each do |file|
|
45
|
+
success = reset_file(file.filename_a)
|
46
|
+
raise "could not reset file #{file.filename_a}" unless success
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.reset_file(filename)
|
51
|
+
system("git reset -q #{filename}")
|
52
|
+
end
|
53
|
+
|
36
54
|
def self.status
|
37
55
|
`git status --porcelain`
|
38
56
|
end
|
@@ -41,8 +59,8 @@ module GitCrecord
|
|
41
59
|
exec('git commit')
|
42
60
|
end
|
43
61
|
|
44
|
-
def self.diff
|
45
|
-
`git diff --no-ext-diff --no-color`
|
62
|
+
def self.diff(staged: false)
|
63
|
+
`git diff --no-ext-diff --no-color #{staged ? '--staged' : ''}`
|
46
64
|
end
|
47
65
|
|
48
66
|
def self.toplevel_dir
|
data/lib/git_crecord/logger.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'logger'
|
2
4
|
|
3
5
|
module GitCrecord
|
4
6
|
LOGGER = Logger.new(File.new(File.join(ENV['HOME'], '.git-crecord.log'), 'w'))
|
5
|
-
LOGGER.formatter = proc{ |_severity, _datetime, _progname, msg| "#{msg}\n" }
|
7
|
+
LOGGER.formatter = proc { |_severity, _datetime, _progname, msg| "#{msg}\n" }
|
6
8
|
LOGGER.level = Logger::INFO
|
7
9
|
end
|
data/lib/git_crecord/ui/color.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'curses'
|
2
4
|
|
3
5
|
module GitCrecord
|
@@ -20,7 +22,7 @@ module GitCrecord
|
|
20
22
|
end
|
21
23
|
|
22
24
|
MAP.each_pair do |name, number|
|
23
|
-
define_singleton_method(name){ Curses.color_pair(number) }
|
25
|
+
define_singleton_method(name) { Curses.color_pair(number) }
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'curses'
|
2
4
|
|
3
5
|
module GitCrecord
|
4
6
|
module UI
|
5
7
|
module HelpWindow
|
6
|
-
CONTENT = <<
|
8
|
+
CONTENT = <<HELP
|
7
9
|
q - quit
|
8
10
|
s - stage selection and quit
|
9
11
|
c - commit selection and quit
|
@@ -20,7 +22,7 @@ module GitCrecord
|
|
20
22
|
A - toggle all selections
|
21
23
|
? - display help
|
22
24
|
R - force redraw
|
23
|
-
|
25
|
+
HELP
|
24
26
|
|
25
27
|
def self.show
|
26
28
|
win = Curses::Window.new(height, width, 0, 0)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'curses'
|
2
4
|
require_relative 'color'
|
3
5
|
require_relative 'help_window'
|
@@ -39,12 +41,13 @@ module GitCrecord
|
|
39
41
|
new_width = Curses.cols
|
40
42
|
new_height = [Curses.lines, content_height(new_width)].max
|
41
43
|
return if width == new_width && @win.maxy == new_height
|
44
|
+
|
42
45
|
@win.resize(new_height, new_width)
|
43
46
|
redraw
|
44
47
|
end
|
45
48
|
|
46
49
|
def content_height(width)
|
47
|
-
@files.reduce(@files.size){ |a, e| a + e.max_height(width) }
|
50
|
+
@files.reduce(@files.size) { |a, e| a + e.max_height(width) }
|
48
51
|
end
|
49
52
|
|
50
53
|
def scroll_position
|
@@ -59,6 +62,7 @@ module GitCrecord
|
|
59
62
|
|
60
63
|
def move_highlight(to)
|
61
64
|
return if to == @highlighted || to.nil?
|
65
|
+
|
62
66
|
from = @highlighted
|
63
67
|
@highlighted = to
|
64
68
|
from.print(self, from.y1 - 1, false)
|
@@ -66,12 +70,13 @@ module GitCrecord
|
|
66
70
|
refresh
|
67
71
|
end
|
68
72
|
|
69
|
-
def addstr(str,
|
70
|
-
@win.setpos(
|
73
|
+
def addstr(str, y_pos = nil, x_pos = 0, attr: 0, fill: false)
|
74
|
+
@win.setpos(y_pos, x_pos) unless y_pos.nil?
|
71
75
|
@win.attrset(attr)
|
72
76
|
@win.addstr(str)
|
73
77
|
fill_size = width - @win.curx
|
74
|
-
return unless fill && fill_size
|
78
|
+
return unless fill && fill_size.positive?
|
79
|
+
|
75
80
|
@win.addstr((fill * fill_size)[0..fill_size])
|
76
81
|
end
|
77
82
|
|
@@ -79,6 +84,7 @@ module GitCrecord
|
|
79
84
|
list.each do |entry|
|
80
85
|
line_number = entry.print(self, line_number, entry == @highlighted)
|
81
86
|
next unless entry.expanded
|
87
|
+
|
82
88
|
line_number = print_list(entry.subs, line_number)
|
83
89
|
addstr('', line_number += 1, fill: '_') if entry.is_a?(Diff::File)
|
84
90
|
end
|
@@ -89,6 +95,7 @@ module GitCrecord
|
|
89
95
|
@visibles = @files.each_with_object([]) do |entry, vs|
|
90
96
|
vs << entry
|
91
97
|
next unless entry.expanded
|
98
|
+
|
92
99
|
entry.selectable_subs.each do |entryy|
|
93
100
|
vs << entryy
|
94
101
|
vs.concat(entryy.selectable_subs) if entryy.expanded
|
@@ -101,11 +108,11 @@ module GitCrecord
|
|
101
108
|
end
|
102
109
|
|
103
110
|
def stage
|
104
|
-
QuitAction.new{ Git.stage(@files) }
|
111
|
+
QuitAction.new { |reverse| Git.stage(@files, reverse) }
|
105
112
|
end
|
106
113
|
|
107
114
|
def commit
|
108
|
-
QuitAction.new{ Git.stage(@files) && Git.commit }
|
115
|
+
QuitAction.new { |reverse| Git.stage(@files, reverse) && Git.commit }
|
109
116
|
end
|
110
117
|
|
111
118
|
def highlight_next
|
@@ -127,14 +134,14 @@ module GitCrecord
|
|
127
134
|
def highlight_next_hunk
|
128
135
|
index = @visibles.index(@highlighted)
|
129
136
|
move_highlight(
|
130
|
-
@visibles[(index + 1)..-1].find{ |entry| !entry.subs.empty? }
|
137
|
+
@visibles[(index + 1)..-1].find { |entry| !entry.subs.empty? }
|
131
138
|
)
|
132
139
|
end
|
133
140
|
|
134
141
|
def highlight_previous_hunk
|
135
142
|
index = @visibles.index(@highlighted)
|
136
143
|
move_highlight(
|
137
|
-
@visibles[0...index].reverse_each.find{ |entry| !entry.subs.empty? }
|
144
|
+
@visibles[0...index].reverse_each.find { |entry| !entry.subs.empty? }
|
138
145
|
)
|
139
146
|
end
|
140
147
|
|
@@ -159,7 +166,7 @@ module GitCrecord
|
|
159
166
|
|
160
167
|
def toggle_all_selections
|
161
168
|
new_selected = @files[0].selected == false
|
162
|
-
@files.each{ |file| file.selected = new_selected }
|
169
|
+
@files.each { |file| file.selected = new_selected }
|
163
170
|
redraw
|
164
171
|
end
|
165
172
|
|
data/lib/git_crecord/ui.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'curses'
|
2
4
|
require_relative 'ui/color'
|
3
5
|
require_relative 'ui/hunks_window'
|
@@ -34,7 +36,7 @@ module GitCrecord
|
|
34
36
|
Curses.clear
|
35
37
|
Curses.noecho
|
36
38
|
Curses.curs_set(0)
|
37
|
-
pad = Curses::Pad.new(1, 1).tap{ |p| p.keypad = true }
|
39
|
+
pad = Curses::Pad.new(1, 1).tap { |p| p.keypad = true }
|
38
40
|
run_loop(HunksWindow.new(pad, files))
|
39
41
|
ensure
|
40
42
|
Curses.close_screen
|
@@ -44,6 +46,7 @@ module GitCrecord
|
|
44
46
|
loop do
|
45
47
|
c = win.getch
|
46
48
|
next if ACTIONS[c].nil?
|
49
|
+
|
47
50
|
quit = win.send(ACTIONS[c])
|
48
51
|
break quit if quit == :quit
|
49
52
|
end
|
data/lib/git_crecord/version.rb
CHANGED
data/lib/git_crecord.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'git_crecord/diff'
|
2
4
|
require_relative 'git_crecord/git'
|
3
5
|
require_relative 'git_crecord/ui'
|
@@ -13,37 +15,47 @@ module GitCrecord
|
|
13
15
|
help
|
14
16
|
true
|
15
17
|
else
|
16
|
-
run(with_untracked_files: untracked_files?(argv))
|
18
|
+
run(with_untracked_files: untracked_files?(argv), reverse: reverse?(argv))
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
20
22
|
def self.untracked_files?(argv)
|
23
|
+
return false if reverse?(argv)
|
24
|
+
|
21
25
|
argv.include?('--untracked-files') || argv.include?('-u')
|
22
26
|
end
|
23
27
|
|
24
|
-
def self.
|
28
|
+
def self.reverse?(argv)
|
29
|
+
argv.include?('--reverse') || argv.include?('-R')
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.run(with_untracked_files: false, reverse: false)
|
25
33
|
toplevel_dir = Git.toplevel_dir
|
26
34
|
return false if toplevel_dir.empty?
|
35
|
+
|
27
36
|
Dir.chdir(toplevel_dir) do
|
28
|
-
files = Diff.parse(Git.diff)
|
37
|
+
files = Diff.parse(Git.diff(staged: reverse), reverse)
|
29
38
|
files.concat(Diff.untracked_files(Git.status)) if with_untracked_files
|
30
39
|
return false if files.empty?
|
40
|
+
|
31
41
|
result = UI.run(files)
|
32
|
-
return result.call == true if result.respond_to?(:call)
|
42
|
+
return result.call(reverse) == true if result.respond_to?(:call)
|
43
|
+
|
33
44
|
true
|
34
45
|
end
|
35
46
|
end
|
36
47
|
|
37
48
|
def self.help
|
38
|
-
puts
|
39
|
-
usage: git crecord [<options>]
|
49
|
+
puts <<~HELP
|
50
|
+
usage: git crecord [<options>]
|
40
51
|
|
41
|
-
|
42
|
-
|
43
|
-
|
52
|
+
-u, --untracked-files -- show untracked files
|
53
|
+
-R, --reverse -- unstage hunks
|
54
|
+
--version -- show version information
|
55
|
+
-h -- this help message
|
44
56
|
|
45
|
-
|
46
|
-
#{UI::HelpWindow::CONTENT.gsub(/^/, ' ')}
|
47
|
-
|
57
|
+
in-program commands:
|
58
|
+
#{UI::HelpWindow::CONTENT.gsub(/^/, ' ')}
|
59
|
+
HELP
|
48
60
|
end
|
49
61
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../../test_helper'
|
2
4
|
|
3
5
|
class HunkTest < Minitest::Test
|
@@ -23,6 +25,6 @@ class HunkTest < Minitest::Test
|
|
23
25
|
|
24
26
|
def test_parse_header_failure
|
25
27
|
hunk = Hunk.new('ugly header')
|
26
|
-
assert_raises(RuntimeError){ hunk.parse_header }
|
28
|
+
assert_raises(RuntimeError) { hunk.parse_header }
|
27
29
|
end
|
28
30
|
end
|
data/test/system-test.sh
CHANGED
@@ -4,13 +4,15 @@ set -euo pipefail
|
|
4
4
|
|
5
5
|
function assert-equal() {
|
6
6
|
local expected=$1
|
7
|
-
local actual=$
|
7
|
+
local actual=$2
|
8
8
|
if test "$expected" != "$actual" ; then
|
9
|
-
cat <<
|
9
|
+
cat << EOF
|
10
10
|
expect:
|
11
|
-
$
|
11
|
+
$expected
|
12
12
|
but got:
|
13
13
|
$actual
|
14
|
+
EOF
|
15
|
+
cat << 'EOF'
|
14
16
|
____ _
|
15
17
|
| _ \ __ _ _ __ (_) ___
|
16
18
|
| |_) / _` | '_ \| |/ __|
|
@@ -31,9 +33,14 @@ function assert-status() {
|
|
31
33
|
assert-equal "$expected" "$(git status -s)"
|
32
34
|
}
|
33
35
|
|
34
|
-
function run-git-crecord(){
|
36
|
+
function run-git-crecord() {
|
35
37
|
local keys=$1
|
36
|
-
"$EXECUTABLE" -u <<<"$keys"
|
38
|
+
"$EXECUTABLE" -u "$@" <<<"$keys"
|
39
|
+
}
|
40
|
+
|
41
|
+
function run-git-crecord-reverse() {
|
42
|
+
local keys=$1
|
43
|
+
"$EXECUTABLE" -R <<<"$keys"
|
37
44
|
}
|
38
45
|
|
39
46
|
readonly HERE="$(dirname "$(readlink -m "${BASH_SOURCE[0]}")")"
|
@@ -68,7 +75,7 @@ assert-diff ""
|
|
68
75
|
git reset > /dev/null
|
69
76
|
|
70
77
|
echo "add first line ----------------------------------------------------------"
|
71
|
-
run-git-crecord "
|
78
|
+
run-git-crecord " ljj s"
|
72
79
|
assert-diff "+This is the second line.
|
73
80
|
+This is line 3.
|
74
81
|
+This is line 4."
|
@@ -76,7 +83,7 @@ assert-diff "+This is the second line.
|
|
76
83
|
git reset > /dev/null
|
77
84
|
|
78
85
|
echo "add another line --------------------------------------------------------"
|
79
|
-
run-git-crecord "
|
86
|
+
run-git-crecord " ljjjj s"
|
80
87
|
assert-diff "+This is line 1.
|
81
88
|
+This is the second line.
|
82
89
|
+This is line 4."
|
@@ -92,8 +99,8 @@ assert-diff ""
|
|
92
99
|
|
93
100
|
git reset > /dev/null
|
94
101
|
|
95
|
-
echo "delete one
|
96
|
-
run-git-crecord "
|
102
|
+
echo "delete one line ---------------------------------------------------------"
|
103
|
+
run-git-crecord " ljjj s"
|
97
104
|
assert-diff "-This is line 1.
|
98
105
|
-This is line 3."
|
99
106
|
|
@@ -125,14 +132,14 @@ assert-diff ""
|
|
125
132
|
git reset > /dev/null
|
126
133
|
|
127
134
|
echo "add lines of second hunk ------------------------------------------------"
|
128
|
-
run-git-crecord "
|
135
|
+
run-git-crecord " ljjjj sq "
|
129
136
|
assert-diff "-This is the second line.
|
130
137
|
+This is line 2."
|
131
138
|
|
132
139
|
git reset > /dev/null
|
133
140
|
|
134
141
|
echo "add some lines of all hunks ---------------------------------------------"
|
135
|
-
run-git-crecord "
|
142
|
+
run-git-crecord " ljjj jj jj j s"
|
136
143
|
assert-diff "-This is the second line.
|
137
144
|
-This is line 11."
|
138
145
|
|
@@ -176,6 +183,19 @@ echo "++++" >> b_file.txt
|
|
176
183
|
run-git-crecord "s"
|
177
184
|
assert-diff ""
|
178
185
|
|
186
|
+
echo "test unstage ------------------------------------------------------------"
|
187
|
+
echo new line1 > new.txt
|
188
|
+
echo new line2 >> new.txt
|
189
|
+
git add new.txt
|
190
|
+
run-git-crecord-reverse 'j k lj Gkljj s'
|
191
|
+
assert-status 'MM a_file.txt
|
192
|
+
M b_file.txt
|
193
|
+
AM new.txt
|
194
|
+
?? sub/'
|
195
|
+
assert-diff '-This is the second line.
|
196
|
+
+This is line 2.
|
197
|
+
+new line2'
|
198
|
+
|
179
199
|
popd > /dev/null # $REPO_DIR
|
180
200
|
|
181
201
|
cat << 'EOF'
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,49 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git-crecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maik Brendler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-11-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: curses
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5.8'
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 5.8.4
|
37
|
+
type: :development
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "~>"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '5.8'
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 5.8.4
|
13
47
|
- !ruby/object:Gem::Dependency
|
14
48
|
name: rake
|
15
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -31,32 +65,25 @@ dependencies:
|
|
31
65
|
- !ruby/object:Gem::Version
|
32
66
|
version: 10.1.1
|
33
67
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
68
|
+
name: rubocop
|
35
69
|
requirement: !ruby/object:Gem::Requirement
|
36
70
|
requirements:
|
37
|
-
- - "~>"
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: '5.8'
|
40
71
|
- - ">="
|
41
72
|
- !ruby/object:Gem::Version
|
42
|
-
version:
|
73
|
+
version: 0.56.0
|
43
74
|
type: :development
|
44
75
|
prerelease: false
|
45
76
|
version_requirements: !ruby/object:Gem::Requirement
|
46
77
|
requirements:
|
47
|
-
- - "~>"
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: '5.8'
|
50
78
|
- - ">="
|
51
79
|
- !ruby/object:Gem::Version
|
52
|
-
version:
|
80
|
+
version: 0.56.0
|
53
81
|
description: This gem adds the git-crecord command. It provides a curses UI to stage/commit
|
54
82
|
git-hunks.
|
55
83
|
email: maik.brendler@invision.de
|
56
84
|
executables:
|
57
85
|
- git-crecord
|
58
|
-
extensions:
|
59
|
-
- ext/mkrf_conf.rb
|
86
|
+
extensions: []
|
60
87
|
extra_rdoc_files: []
|
61
88
|
files:
|
62
89
|
- ".gitignore"
|
@@ -66,7 +93,6 @@ files:
|
|
66
93
|
- README.md
|
67
94
|
- Rakefile
|
68
95
|
- bin/git-crecord
|
69
|
-
- ext/mkrf_conf.rb
|
70
96
|
- git-crecord.gemspec
|
71
97
|
- lib/git_crecord.rb
|
72
98
|
- lib/git_crecord/diff.rb
|
@@ -99,7 +125,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
99
125
|
requirements:
|
100
126
|
- - ">="
|
101
127
|
- !ruby/object:Gem::Version
|
102
|
-
version: 2.
|
128
|
+
version: 2.3.0
|
103
129
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
130
|
requirements:
|
105
131
|
- - ">="
|
@@ -107,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
133
|
version: 1.3.6
|
108
134
|
requirements: []
|
109
135
|
rubyforge_project:
|
110
|
-
rubygems_version: 2.6
|
136
|
+
rubygems_version: 2.7.6
|
111
137
|
signing_key:
|
112
138
|
specification_version: 4
|
113
139
|
summary: Git command to stage/commit hunks the simple way.
|
data/ext/mkrf_conf.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'rubygems/dependency_installer'
|
3
|
-
|
4
|
-
# This is a hack to not install curses for ruby-2.0.
|
5
|
-
|
6
|
-
di = Gem::DependencyInstaller.new
|
7
|
-
|
8
|
-
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.1.0')
|
9
|
-
puts 'install curses'
|
10
|
-
di.install 'curses', '~>1.0'
|
11
|
-
end
|
12
|
-
|
13
|
-
File.open(File.join(__dir__, 'Rakefile'), 'w') do |f|
|
14
|
-
f.write("task :default#{$/}")
|
15
|
-
end
|