git-crecord 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/.gitignore +3 -0
- data/.rubocop.yml +20 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +50 -0
- data/Rakefile +14 -0
- data/bin/git-crecord +4 -0
- data/ext/mkrf_conf.rb +10 -0
- data/git-crecord.gemspec +29 -0
- data/lib/git_crecord/diff/difference.rb +81 -0
- data/lib/git_crecord/diff/file.rb +68 -0
- data/lib/git_crecord/diff/hunk.rb +49 -0
- data/lib/git_crecord/diff/line.rb +57 -0
- data/lib/git_crecord/diff.rb +59 -0
- data/lib/git_crecord/git.rb +52 -0
- data/lib/git_crecord/logger.rb +7 -0
- data/lib/git_crecord/quit_action.rb +8 -0
- data/lib/git_crecord/ui/color.rb +27 -0
- data/lib/git_crecord/ui/help_window.rb +45 -0
- data/lib/git_crecord/ui/hunks_window.rb +172 -0
- data/lib/git_crecord/ui.rb +51 -0
- data/lib/git_crecord/version.rb +3 -0
- data/lib/git_crecord.rb +45 -0
- data/screenshot.jpg +0 -0
- data/test/git_crecord/diff/hunk_test.rb +28 -0
- data/test/system-test.sh +187 -0
- data/test/test_helper.rb +3 -0
- metadata +117 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fffb4cbf34ec7e9c44ff7c17f598c2a2164adf03
|
4
|
+
data.tar.gz: 396608cc59e9a52e8315c4a35f68827693030d0f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f80d28b331abbb5dfe6387718fc37032f4329dab9dc3a6547108aeb8453139c8139b017891c60b858c41e091bb52bd18bb447531a5a0ea0df84244d9023e1458
|
7
|
+
data.tar.gz: 3377110378bbe79cbb61b9d3707171a0b0eb56847bdfcbb6fa92df4679fbd543ce89124d87dd42648d0075c8c301d6fe4f4fc2beaab1eadb8aa257500d9b3023
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,20 @@
|
|
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
|
+
Documentation:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
AllCops:
|
20
|
+
DisplayStyleGuide: true
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016 Maik Brendler
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# git-crecord
|
2
|
+
|
3
|
+
Inspred by [crecord mercurial extension](https://bitbucket.org/edgimar/crecord/wiki/Home), git-crecord is an easy way for partially committing/staging of git changes.
|
4
|
+
|
5
|
+

|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
TODO
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
```shell
|
14
|
+
$ git crecord
|
15
|
+
```
|
16
|
+
|
17
|
+
Key-bindings:
|
18
|
+
```
|
19
|
+
q - quit
|
20
|
+
s - stage selection and quit
|
21
|
+
c - commit selection and quit
|
22
|
+
j / ↓ - down
|
23
|
+
k / ↑ - up
|
24
|
+
h / ← - collapse hunk
|
25
|
+
l / → - expand
|
26
|
+
f - toggle fold
|
27
|
+
g - go to first line
|
28
|
+
G - go to last line
|
29
|
+
C-P - up to previous hunk / file
|
30
|
+
C-N - down to previous hunk / file
|
31
|
+
SPACE - toggle selection
|
32
|
+
A - toggle all selections
|
33
|
+
? - display help
|
34
|
+
R - force redraw
|
35
|
+
```
|
36
|
+
|
37
|
+
## Development
|
38
|
+
|
39
|
+
```shell
|
40
|
+
$ git clone https://github.com/mbrendler/git-crecord
|
41
|
+
$ cd git-crecord
|
42
|
+
$ bundle install
|
43
|
+
$ ln -s bin/git-crecord /usr/bin/git-crecord
|
44
|
+
```
|
45
|
+
|
46
|
+
Tests:
|
47
|
+
```shell
|
48
|
+
$ bundle exec rake test
|
49
|
+
$ bundle exec rake systemtest
|
50
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
t.test_files = FileList['test/git_crecord/**/*test.rb']
|
7
|
+
end
|
8
|
+
|
9
|
+
desc 'run system tests'
|
10
|
+
task :systemtest do
|
11
|
+
sh(File.join(__dir__, 'test/system-test.sh'))
|
12
|
+
end
|
13
|
+
|
14
|
+
task :default # dummy task to build native extension (install curses)
|
data/bin/git-crecord
ADDED
data/ext/mkrf_conf.rb
ADDED
data/git-crecord.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'lib/git_crecord/version'
|
2
|
+
|
3
|
+
GemSpec = Gem::Specification.new do |spec|
|
4
|
+
spec.required_rubygems_version = Gem::Requirement.new('>= 1.3.6')
|
5
|
+
spec.platform = Gem::Platform::RUBY
|
6
|
+
spec.required_ruby_version = '>= 2.0.0'
|
7
|
+
spec.name = 'git-crecord'
|
8
|
+
spec.version = GitCrecord::VERSION
|
9
|
+
spec.authors = 'Maik Brendler'
|
10
|
+
spec.email = 'maik.brendler@invision.de'
|
11
|
+
spec.summary = 'Git command to stage/commit hunks the simple way.'
|
12
|
+
spec.description = %w(
|
13
|
+
This gem adds the git-crecord command.
|
14
|
+
It provides a curses UI to stage/commit git-hunks.
|
15
|
+
).join(' ')
|
16
|
+
spec.license = 'MIT'
|
17
|
+
spec.homepage = 'https://github.com/mbrendler/git-crecord'
|
18
|
+
spec.metadata = {
|
19
|
+
'issue_tracker' => 'https://github.com/mbrendler/git-crecord/issues'
|
20
|
+
}
|
21
|
+
spec.require_paths = %w(lib)
|
22
|
+
spec.files = `git ls-files`.split($RS).delete_if{ |f| %r{^(spec|test)/} =~ f }
|
23
|
+
spec.test_files = `git ls-files`.split($RS).grep(%r{^(spec|test)/})
|
24
|
+
spec.executables = %w(git-crecord)
|
25
|
+
spec.has_rdoc = false
|
26
|
+
spec.add_development_dependency 'rake', '~> 10.1', '>= 10.1.1'
|
27
|
+
spec.add_development_dependency 'minitest', '~> 5.8', '>= 5.8.4'
|
28
|
+
spec.extensions << 'ext/mkrf_conf.rb' # install curses dependency
|
29
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative '../ui/color'
|
2
|
+
|
3
|
+
module GitCrecord
|
4
|
+
module Diff
|
5
|
+
class Difference
|
6
|
+
attr_accessor :expanded
|
7
|
+
attr_accessor :y1, :y2
|
8
|
+
attr_reader :subs
|
9
|
+
|
10
|
+
SELECTED_MAP = {
|
11
|
+
true => '[X] ',
|
12
|
+
false => '[ ] ',
|
13
|
+
:partly => '[~] '
|
14
|
+
}.freeze
|
15
|
+
SELECTION_MARKER_WIDTH = SELECTED_MAP[true].size
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@subs = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def strings(width)
|
22
|
+
to_s.scan(/.{1,#{content_width(width)}}/)
|
23
|
+
end
|
24
|
+
|
25
|
+
def max_height(width)
|
26
|
+
width = content_width(width)
|
27
|
+
((to_s.size - 1).abs / width) + 1 + subs.reduce(0) do |a, e|
|
28
|
+
a + e.max_height(width)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def content_width(width)
|
33
|
+
[1, width - x_offset - SELECTION_MARKER_WIDTH].max
|
34
|
+
end
|
35
|
+
|
36
|
+
def selectable?
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
def selectable_subs
|
41
|
+
@selectable_subs ||= subs.select(&:selectable?)
|
42
|
+
end
|
43
|
+
|
44
|
+
def selected
|
45
|
+
s = selectable_subs.map(&:selected).uniq
|
46
|
+
return s[0] if s.size == 1
|
47
|
+
:partly
|
48
|
+
end
|
49
|
+
|
50
|
+
def selected=(value)
|
51
|
+
selectable_subs.each{ |sub| sub.selected = value }
|
52
|
+
end
|
53
|
+
|
54
|
+
def style(is_highlighted)
|
55
|
+
return Curses::A_BOLD | UI::Color.hl if is_highlighted
|
56
|
+
Curses::A_BOLD | UI::Color.normal
|
57
|
+
end
|
58
|
+
|
59
|
+
def prefix_style(_is_highlighted)
|
60
|
+
UI::Color.normal
|
61
|
+
end
|
62
|
+
|
63
|
+
def prefix(line_number)
|
64
|
+
return SELECTED_MAP.fetch(selected) if line_number == 0 && selectable?
|
65
|
+
' ' * SELECTION_MARKER_WIDTH
|
66
|
+
end
|
67
|
+
|
68
|
+
def print(win, line_number, is_highlighted)
|
69
|
+
@y1 = line_number + 1
|
70
|
+
prefix_style = prefix_style(is_highlighted)
|
71
|
+
style = style(is_highlighted)
|
72
|
+
strings(win.width).each_with_index do |string, index|
|
73
|
+
win.addstr(' ' * x_offset, line_number += 1, attr: prefix_style)
|
74
|
+
win.addstr(prefix(index), attr: prefix_style)
|
75
|
+
win.addstr(string, attr: style, fill: ' ')
|
76
|
+
end
|
77
|
+
@y2 = line_number
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative 'difference'
|
2
|
+
require_relative 'hunk'
|
3
|
+
require_relative '../ui/color'
|
4
|
+
|
5
|
+
module GitCrecord
|
6
|
+
module Diff
|
7
|
+
class File < Difference
|
8
|
+
attr_reader :filename_a
|
9
|
+
attr_reader :type
|
10
|
+
|
11
|
+
def initialize(filename_a, filename_b, type: :modified)
|
12
|
+
@filename_a = filename_a
|
13
|
+
@filename_b = filename_b
|
14
|
+
@type = type
|
15
|
+
@expanded = false
|
16
|
+
super()
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
prefix = {modified: 'M', untracked: '?'}.fetch(type)
|
21
|
+
return "#{prefix} #{@filename_a}" if @filename_a == @filename_b
|
22
|
+
"#{prefix} #{filename_a} -> #{filename_b}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def info_string
|
26
|
+
line_count = subs.reduce(0){ |a, e| e.selectable_subs.size + a }
|
27
|
+
" #{subs.size} hunk(s), #{line_count} line(s) changed"
|
28
|
+
end
|
29
|
+
|
30
|
+
def strings(width)
|
31
|
+
result = super
|
32
|
+
return result unless expanded
|
33
|
+
result += info_string.scan(/.{1,#{content_width(width)}}/)
|
34
|
+
result << ''
|
35
|
+
end
|
36
|
+
|
37
|
+
def max_height(width)
|
38
|
+
super + ((info_string.size - 1).abs / content_width(width)) + 2
|
39
|
+
end
|
40
|
+
|
41
|
+
def x_offset
|
42
|
+
0
|
43
|
+
end
|
44
|
+
|
45
|
+
def <<(hunk)
|
46
|
+
subs << Hunk.new(hunk)
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_hunk_line(line)
|
51
|
+
subs.last << line
|
52
|
+
end
|
53
|
+
|
54
|
+
def generate_diff
|
55
|
+
return unless selected
|
56
|
+
[
|
57
|
+
"diff --git a/#{@filename_a} b/#{@filename_b}",
|
58
|
+
"--- a/#{@filename_a}",
|
59
|
+
"+++ b/#{@filename_b}",
|
60
|
+
*subs.map(&:generate_diff).compact,
|
61
|
+
''
|
62
|
+
].join("\n")
|
63
|
+
end
|
64
|
+
|
65
|
+
alias prefix_style style
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative 'difference'
|
2
|
+
require_relative 'line'
|
3
|
+
require_relative '../ui/color'
|
4
|
+
|
5
|
+
module GitCrecord
|
6
|
+
module Diff
|
7
|
+
class Hunk < Difference
|
8
|
+
def initialize(head)
|
9
|
+
@head = head
|
10
|
+
@expanded = true
|
11
|
+
super()
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
@head
|
16
|
+
end
|
17
|
+
|
18
|
+
def x_offset
|
19
|
+
3
|
20
|
+
end
|
21
|
+
|
22
|
+
def <<(line)
|
23
|
+
subs << Line.new(line)
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_diff
|
28
|
+
return nil unless selected
|
29
|
+
[generate_header, *subs.map(&:generate_diff).compact].join("\n")
|
30
|
+
end
|
31
|
+
|
32
|
+
def generate_header
|
33
|
+
old_start, old_count, new_start, new_count = parse_header
|
34
|
+
selectable_subs.each do |sub|
|
35
|
+
next if sub.selected
|
36
|
+
new_count -= 1 if sub.add?
|
37
|
+
new_count += 1 if sub.del?
|
38
|
+
end
|
39
|
+
"@@ -#{old_start},#{old_count} +#{new_start},#{new_count} @@"
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_header
|
43
|
+
match = @head.match(/@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@/)
|
44
|
+
raise "mismatching hunk-header - '#{@head}'" if match.nil?
|
45
|
+
[match[1], match[3] || 1, match[4], match[6] || 1].map(&:to_i)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require_relative 'difference'
|
2
|
+
require_relative '../ui/color'
|
3
|
+
|
4
|
+
module GitCrecord
|
5
|
+
module Diff
|
6
|
+
class Line < Difference
|
7
|
+
attr_reader :selected
|
8
|
+
|
9
|
+
def initialize(line)
|
10
|
+
@line = line
|
11
|
+
@selected = true
|
12
|
+
super()
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
@line
|
17
|
+
end
|
18
|
+
|
19
|
+
def x_offset
|
20
|
+
6
|
21
|
+
end
|
22
|
+
|
23
|
+
def add?
|
24
|
+
@line.start_with?('+')
|
25
|
+
end
|
26
|
+
|
27
|
+
def del?
|
28
|
+
@line.start_with?('-')
|
29
|
+
end
|
30
|
+
|
31
|
+
def selectable?
|
32
|
+
add? || del?
|
33
|
+
end
|
34
|
+
|
35
|
+
def selected=(value)
|
36
|
+
@selected = selectable? ? value : selected
|
37
|
+
end
|
38
|
+
|
39
|
+
def expanded
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
43
|
+
def generate_diff
|
44
|
+
return " #{@line[1..-1]}" if !selected && del?
|
45
|
+
return @line if selected
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def style(is_highlighted)
|
50
|
+
return UI::Color.hl if is_highlighted
|
51
|
+
return UI::Color.green if add?
|
52
|
+
return UI::Color.red if del?
|
53
|
+
UI::Color.normal
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require_relative 'diff/file'
|
2
|
+
|
3
|
+
module GitCrecord
|
4
|
+
module Diff
|
5
|
+
def self.parse(diff)
|
6
|
+
files = []
|
7
|
+
enum = diff.lines.each
|
8
|
+
loop do
|
9
|
+
line = enum.next
|
10
|
+
line.chomp!
|
11
|
+
next files << parse_file_header(line, enum) if file_start?(line)
|
12
|
+
next files[-1] << line if hunk_start?(line)
|
13
|
+
files[-1].add_hunk_line(line)
|
14
|
+
end
|
15
|
+
files
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.file_start?(line)
|
19
|
+
line.start_with?('diff')
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.hunk_start?(line)
|
23
|
+
line.start_with?('@@')
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.parse_file_header(line, enum)
|
27
|
+
enum.next # index ...
|
28
|
+
enum.next # --- ...
|
29
|
+
enum.next # +++ ...
|
30
|
+
File.new(*parse_filenames(line))
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.parse_filenames(line)
|
34
|
+
line.match(%r{a/(.*) b/(.*)$})[1..2]
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.untracked_files(git_status)
|
38
|
+
git_status.lines.select{ |l| l.start_with?('??') }.flat_map do |path|
|
39
|
+
path = path.chomp[3..-1]
|
40
|
+
::File.directory?(path) ? untracked_dir(path) : untracked_file(path)
|
41
|
+
end.compact
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.untracked_file(filename)
|
45
|
+
File.new(filename, filename, type: :untracked).tap do |file|
|
46
|
+
file_lines = ::File.readlines(filename)
|
47
|
+
file << "@@ -0,0 +1,#{file_lines.size} @@"
|
48
|
+
file_lines.each{ |line| file.add_hunk_line("+#{line.chomp}") }
|
49
|
+
file.selected = false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.untracked_dir(path)
|
54
|
+
Dir.glob(::File.join(path, '**/*')).map do |filename|
|
55
|
+
untracked_file(filename)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative 'logger'
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
module GitCrecord
|
5
|
+
module Git
|
6
|
+
def self.stage(files)
|
7
|
+
selected_files = files.select(&:selected)
|
8
|
+
add_files(selected_files.select{ |file| file.type == :untracked })
|
9
|
+
diff = selected_files.map(&:generate_diff).join("\n")
|
10
|
+
_stage(diff).success?
|
11
|
+
end
|
12
|
+
|
13
|
+
def self._stage(diff)
|
14
|
+
cmd = 'git apply --cached --unidiff-zero - '
|
15
|
+
content, status = Open3.capture2e(cmd, stdin_data: diff)
|
16
|
+
LOGGER.info(cmd)
|
17
|
+
LOGGER.info(diff)
|
18
|
+
LOGGER.info(diff.lines.size)
|
19
|
+
LOGGER.info('stdout/stderr:')
|
20
|
+
LOGGER.info(content)
|
21
|
+
LOGGER.info("return code: #{status}")
|
22
|
+
status
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.add_files(files)
|
26
|
+
files.each do |file|
|
27
|
+
success = add_file(file.filename_a)
|
28
|
+
raise "could not add file #{file.filename_a}" unless success
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.add_file(filename)
|
33
|
+
system("git add -N #{filename}")
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.status
|
37
|
+
`git status --porcelain`
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.commit
|
41
|
+
exec('git commit')
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.diff
|
45
|
+
`git diff --no-ext-diff --no-color`
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.toplevel_dir
|
49
|
+
`git rev-parse --show-toplevel`.chomp
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'curses'
|
2
|
+
|
3
|
+
module GitCrecord
|
4
|
+
module UI
|
5
|
+
module Color
|
6
|
+
MAP = {
|
7
|
+
normal: 1,
|
8
|
+
green: 2,
|
9
|
+
red: 3,
|
10
|
+
hl: 4
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
def self.init
|
14
|
+
Curses.start_color
|
15
|
+
Curses.use_default_colors
|
16
|
+
Curses.init_pair(MAP[:normal], -1, -1)
|
17
|
+
Curses.init_pair(MAP[:green], Curses::COLOR_GREEN, -1)
|
18
|
+
Curses.init_pair(MAP[:red], Curses::COLOR_RED, -1)
|
19
|
+
Curses.init_pair(MAP[:hl], Curses::COLOR_WHITE, Curses::COLOR_GREEN)
|
20
|
+
end
|
21
|
+
|
22
|
+
MAP.each_pair do |name, number|
|
23
|
+
define_singleton_method(name){ Curses.color_pair(number) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'curses'
|
2
|
+
|
3
|
+
module GitCrecord
|
4
|
+
module UI
|
5
|
+
module HelpWindow
|
6
|
+
CONTENT = <<EOS.freeze
|
7
|
+
q - quit
|
8
|
+
s - stage selection and quit
|
9
|
+
c - commit selection and quit
|
10
|
+
j / ↓ - down
|
11
|
+
k / ↑ - up
|
12
|
+
h / ← - collapse hunk
|
13
|
+
l / → - expand
|
14
|
+
f - toggle fold
|
15
|
+
g - go to first line
|
16
|
+
G - go to last line
|
17
|
+
C-P - up to previous hunk / file
|
18
|
+
C-N - down to previous hunk / file
|
19
|
+
SPACE - toggle selection
|
20
|
+
A - toggle all selections
|
21
|
+
? - display help
|
22
|
+
R - force redraw
|
23
|
+
EOS
|
24
|
+
|
25
|
+
def self.show
|
26
|
+
win = Curses::Window.new(height, width, 0, 0)
|
27
|
+
win.box('|', '-')
|
28
|
+
CONTENT.split("\n").each_with_index do |line, index|
|
29
|
+
win.setpos(index + 1, 1)
|
30
|
+
win.addstr(line)
|
31
|
+
end
|
32
|
+
win.getch
|
33
|
+
win.close
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.width
|
37
|
+
CONTENT.lines.map(&:size).max + 3
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.height
|
41
|
+
CONTENT.lines.size + 2
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'curses'
|
2
|
+
require_relative 'color'
|
3
|
+
require_relative 'help_window'
|
4
|
+
require_relative '../git'
|
5
|
+
require_relative '../quit_action'
|
6
|
+
|
7
|
+
module GitCrecord
|
8
|
+
module UI
|
9
|
+
class HunksWindow
|
10
|
+
def initialize(win, files)
|
11
|
+
@win = win
|
12
|
+
@files = files
|
13
|
+
@visibles = @files
|
14
|
+
@highlighted = @files[0]
|
15
|
+
@scroll_position = 0
|
16
|
+
|
17
|
+
resize
|
18
|
+
end
|
19
|
+
|
20
|
+
def getch
|
21
|
+
@win.getch
|
22
|
+
end
|
23
|
+
|
24
|
+
def width
|
25
|
+
@win.maxx
|
26
|
+
end
|
27
|
+
|
28
|
+
def refresh
|
29
|
+
@win.refresh(scroll_position, 0, 0, 0, Curses.lines - 1, width)
|
30
|
+
end
|
31
|
+
|
32
|
+
def redraw
|
33
|
+
@win.clear
|
34
|
+
print_list(@files)
|
35
|
+
refresh
|
36
|
+
end
|
37
|
+
|
38
|
+
def resize
|
39
|
+
new_width = Curses.cols
|
40
|
+
new_height = [Curses.lines, content_height(new_width)].max
|
41
|
+
return if width == new_width && @win.maxy == new_height
|
42
|
+
@win.resize(new_height, new_width)
|
43
|
+
redraw
|
44
|
+
end
|
45
|
+
|
46
|
+
def content_height(width)
|
47
|
+
@files.reduce(@files.size){ |a, e| a + e.max_height(width) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def scroll_position
|
51
|
+
upper_position = @highlighted.y1 - 3
|
52
|
+
if @scroll_position > upper_position
|
53
|
+
@scroll_position = upper_position
|
54
|
+
elsif @scroll_position <= @highlighted.y2 + 4 - Curses.lines
|
55
|
+
@scroll_position = [@highlighted.y2 + 4, @win.maxy].min - Curses.lines
|
56
|
+
end
|
57
|
+
@scroll_position
|
58
|
+
end
|
59
|
+
|
60
|
+
def move_highlight(to)
|
61
|
+
return if to == @highlighted || to.nil?
|
62
|
+
from = @highlighted
|
63
|
+
@highlighted = to
|
64
|
+
from.print(self, from.y1 - 1, false)
|
65
|
+
to.print(self, to.y1 - 1, true)
|
66
|
+
refresh
|
67
|
+
end
|
68
|
+
|
69
|
+
def addstr(str, y = nil, x = 0, attr: 0, fill: false)
|
70
|
+
@win.setpos(y, x) unless y.nil?
|
71
|
+
@win.attrset(attr)
|
72
|
+
@win.addstr(str)
|
73
|
+
fill_size = width - @win.curx
|
74
|
+
return unless fill && fill_size > 0
|
75
|
+
@win.addstr((fill * fill_size)[0..fill_size])
|
76
|
+
end
|
77
|
+
|
78
|
+
def print_list(list, line_number = -1)
|
79
|
+
list.each do |entry|
|
80
|
+
line_number = entry.print(self, line_number, entry == @highlighted)
|
81
|
+
next unless entry.expanded
|
82
|
+
line_number = print_list(entry.subs, line_number)
|
83
|
+
addstr('', line_number += 1, fill: '_') if entry.is_a?(Diff::File)
|
84
|
+
end
|
85
|
+
line_number
|
86
|
+
end
|
87
|
+
|
88
|
+
def update_visibles
|
89
|
+
@visibles = @files.each_with_object([]) do |entry, vs|
|
90
|
+
vs << entry
|
91
|
+
next unless entry.expanded
|
92
|
+
entry.selectable_subs.each do |entryy|
|
93
|
+
vs << entryy
|
94
|
+
vs.concat(entryy.selectable_subs) if entryy.expanded
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def quit
|
100
|
+
:quit
|
101
|
+
end
|
102
|
+
|
103
|
+
def stage
|
104
|
+
QuitAction.new{ Git.stage(@files) }
|
105
|
+
end
|
106
|
+
|
107
|
+
def commit
|
108
|
+
QuitAction.new{ Git.stage(@files) && Git.commit }
|
109
|
+
end
|
110
|
+
|
111
|
+
def highlight_next
|
112
|
+
move_highlight(@visibles[@visibles.index(@highlighted) + 1])
|
113
|
+
end
|
114
|
+
|
115
|
+
def highlight_previous
|
116
|
+
move_highlight(@visibles[[@visibles.index(@highlighted) - 1, 0].max])
|
117
|
+
end
|
118
|
+
|
119
|
+
def highlight_first
|
120
|
+
move_highlight(@visibles[0])
|
121
|
+
end
|
122
|
+
|
123
|
+
def highlight_last
|
124
|
+
move_highlight(@visibles[-1])
|
125
|
+
end
|
126
|
+
|
127
|
+
def highlight_next_hunk
|
128
|
+
index = @visibles.index(@highlighted)
|
129
|
+
move_highlight(
|
130
|
+
@visibles[(index + 1)..-1].find{ |entry| !entry.subs.empty? }
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
134
|
+
def highlight_previous_hunk
|
135
|
+
index = @visibles.index(@highlighted)
|
136
|
+
move_highlight(
|
137
|
+
@visibles[0...index].reverse_each.find{ |entry| !entry.subs.empty? }
|
138
|
+
)
|
139
|
+
end
|
140
|
+
|
141
|
+
def collapse
|
142
|
+
toggle_fold if !@highlighted.subs.empty? && @highlighted.expanded
|
143
|
+
end
|
144
|
+
|
145
|
+
def expand
|
146
|
+
toggle_fold if !@highlighted.subs.empty? && !@highlighted.expanded
|
147
|
+
end
|
148
|
+
|
149
|
+
def toggle_fold
|
150
|
+
@highlighted.expanded = !@highlighted.expanded
|
151
|
+
update_visibles
|
152
|
+
redraw
|
153
|
+
end
|
154
|
+
|
155
|
+
def toggle_selection
|
156
|
+
@highlighted.selected = !@highlighted.selected
|
157
|
+
redraw
|
158
|
+
end
|
159
|
+
|
160
|
+
def toggle_all_selections
|
161
|
+
new_selected = @files[0].selected == false
|
162
|
+
@files.each{ |file| file.selected = new_selected }
|
163
|
+
redraw
|
164
|
+
end
|
165
|
+
|
166
|
+
def help_window
|
167
|
+
HelpWindow.show
|
168
|
+
refresh
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'curses'
|
2
|
+
require_relative 'ui/color'
|
3
|
+
require_relative 'ui/hunks_window'
|
4
|
+
|
5
|
+
module GitCrecord
|
6
|
+
module UI
|
7
|
+
ACTIONS = {
|
8
|
+
'q' => :quit,
|
9
|
+
's' => :stage,
|
10
|
+
'c' => :commit,
|
11
|
+
'j' => :highlight_next,
|
12
|
+
Curses::KEY_DOWN => :highlight_next,
|
13
|
+
'k' => :highlight_previous,
|
14
|
+
Curses::KEY_UP => :highlight_previous,
|
15
|
+
'h' => :collapse,
|
16
|
+
Curses::KEY_LEFT => :collapse,
|
17
|
+
'l' => :expand,
|
18
|
+
Curses::KEY_RIGHT => :expand,
|
19
|
+
'f' => :toggle_fold,
|
20
|
+
'g' => :highlight_first,
|
21
|
+
'G' => :highlight_last,
|
22
|
+
''.ord => :highlight_next_hunk,
|
23
|
+
''.ord => :highlight_previous_hunk,
|
24
|
+
' ' => :toggle_selection,
|
25
|
+
'A' => :toggle_all_selections,
|
26
|
+
'?' => :help_window,
|
27
|
+
'R' => :redraw,
|
28
|
+
Curses::KEY_RESIZE => :resize
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
def self.run(files)
|
32
|
+
Curses.init_screen.keypad = true
|
33
|
+
Color.init
|
34
|
+
Curses.clear
|
35
|
+
Curses.noecho
|
36
|
+
Curses.curs_set(0)
|
37
|
+
run_loop(HunksWindow.new(Curses::Pad.new(1, 1), files))
|
38
|
+
ensure
|
39
|
+
Curses.close_screen
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.run_loop(win)
|
43
|
+
loop do
|
44
|
+
c = win.getch
|
45
|
+
next if ACTIONS[c].nil?
|
46
|
+
quit = win.send(ACTIONS[c])
|
47
|
+
break quit if quit == :quit
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/git_crecord.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative 'git_crecord/git'
|
2
|
+
require_relative 'git_crecord/diff'
|
3
|
+
require_relative 'git_crecord/ui'
|
4
|
+
require_relative 'git_crecord/version'
|
5
|
+
require_relative 'git_crecord/ui/help_window'
|
6
|
+
|
7
|
+
module GitCrecord
|
8
|
+
def self.main(argv)
|
9
|
+
if argv.include?('--version')
|
10
|
+
puts VERSION
|
11
|
+
true
|
12
|
+
elsif argv.include?('--help') || argv.include?('-h')
|
13
|
+
help
|
14
|
+
true
|
15
|
+
else
|
16
|
+
run(with_untracked_files: !argv.include?('--no-untracked-files'))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.run(with_untracked_files: false)
|
21
|
+
toplevel_dir = Git.toplevel_dir
|
22
|
+
return false if toplevel_dir.empty?
|
23
|
+
Dir.chdir(toplevel_dir) do
|
24
|
+
files = Diff.parse(Git.diff)
|
25
|
+
files.concat(Diff.untracked_files(Git.status)) if with_untracked_files
|
26
|
+
return false if files.empty?
|
27
|
+
result = UI.run(files)
|
28
|
+
return result.call == true if result.respond_to?(:call)
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.help
|
34
|
+
puts <<EOS
|
35
|
+
usage: git crecord [<options>]'
|
36
|
+
|
37
|
+
--no-untracked-files -- ignore untracked files
|
38
|
+
--version -- show version information'
|
39
|
+
-h -- this help message'
|
40
|
+
|
41
|
+
in-program commands:'
|
42
|
+
#{UI::HelpWindow::CONTENT.gsub(/^/, ' ')}
|
43
|
+
EOS
|
44
|
+
end
|
45
|
+
end
|
data/screenshot.jpg
ADDED
Binary file
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative '../../test_helper'
|
2
|
+
|
3
|
+
class HunkTest < Minitest::Test
|
4
|
+
include GitCrecord::Diff
|
5
|
+
|
6
|
+
def test_strings
|
7
|
+
hunk = Hunk.new('1234567890' * 5)
|
8
|
+
expected = %w(12345678901 23456789012 34567890123 45678901234 567890)
|
9
|
+
assert_equal(expected, hunk.strings(19))
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_max_height
|
13
|
+
assert_equal(1, Hunk.new('').max_height(10))
|
14
|
+
assert_equal(1, Hunk.new('1234567890').max_height(18))
|
15
|
+
assert_equal(2, Hunk.new('12345678901').max_height(18))
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_parse_header
|
19
|
+
assert_equal([1, 2, 3, 4], Hunk.new('@@ -1,2 +3,4 @@').parse_header)
|
20
|
+
assert_equal([1, 1, 3, 4], Hunk.new('@@ -1 +3,4 @@').parse_header)
|
21
|
+
assert_equal([1, 2, 3, 1], Hunk.new('@@ -1,2 +3 @@').parse_header)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_parse_header_failure
|
25
|
+
hunk = Hunk.new('ugly header')
|
26
|
+
assert_raises(RuntimeError){ hunk.parse_header }
|
27
|
+
end
|
28
|
+
end
|
data/test/system-test.sh
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
#! /usr/bin/env bash
|
2
|
+
|
3
|
+
set -euo pipefail
|
4
|
+
|
5
|
+
function assert-equal() {
|
6
|
+
local expected=$1
|
7
|
+
local actual=$1
|
8
|
+
if test "$expected" != "$actual" ; then
|
9
|
+
cat << 'EOF'
|
10
|
+
expect:
|
11
|
+
$expect
|
12
|
+
but got:
|
13
|
+
$actual
|
14
|
+
____ _
|
15
|
+
| _ \ __ _ _ __ (_) ___
|
16
|
+
| |_) / _` | '_ \| |/ __|
|
17
|
+
| __/ (_| | | | | | (__
|
18
|
+
|_| \__,_|_| |_|_|\___|
|
19
|
+
EOF
|
20
|
+
exit 1
|
21
|
+
fi
|
22
|
+
}
|
23
|
+
|
24
|
+
function assert-diff(){
|
25
|
+
local expected=$1
|
26
|
+
assert-equal "$expected" "$(git diff | grep '^[+-][^+-]')"
|
27
|
+
}
|
28
|
+
|
29
|
+
function assert-status() {
|
30
|
+
local expected=$1
|
31
|
+
assert-equal "$expected" "$(git status -s)"
|
32
|
+
}
|
33
|
+
|
34
|
+
function run-git-crecord(){
|
35
|
+
local keys=$1
|
36
|
+
"$EXECUTABLE" <<<"$keys"
|
37
|
+
}
|
38
|
+
|
39
|
+
readonly HERE="$(dirname "$(readlink -m "${BASH_SOURCE[0]}")")"
|
40
|
+
readonly TEST_DIR=$HERE/../tmp/__test__
|
41
|
+
readonly EXECUTABLE=$HERE/../bin/git-crecord
|
42
|
+
readonly REPO_DIR=$TEST_DIR/repo
|
43
|
+
|
44
|
+
rm -rf "$TEST_DIR"
|
45
|
+
mkdir -p "$TEST_DIR"
|
46
|
+
|
47
|
+
git init "$REPO_DIR" > /dev/null
|
48
|
+
|
49
|
+
pushd "$REPO_DIR" > /dev/null
|
50
|
+
|
51
|
+
touch a_file.txt
|
52
|
+
|
53
|
+
git add a_file.txt
|
54
|
+
git ci -m 'add a_file.txt' > /dev/null
|
55
|
+
|
56
|
+
cat > a_file.txt << 'EOF'
|
57
|
+
This is line 1.
|
58
|
+
This is the second line.
|
59
|
+
This is line 3.
|
60
|
+
This is line 4.
|
61
|
+
EOF
|
62
|
+
|
63
|
+
|
64
|
+
echo "add all -----------------------------------------------------------------"
|
65
|
+
run-git-crecord "s"
|
66
|
+
assert-diff ""
|
67
|
+
|
68
|
+
git reset > /dev/null
|
69
|
+
|
70
|
+
echo "add first line ----------------------------------------------------------"
|
71
|
+
run-git-crecord " lj s"
|
72
|
+
assert-diff "+This is the second line.
|
73
|
+
+This is line 3.
|
74
|
+
+This is line 4."
|
75
|
+
|
76
|
+
git reset > /dev/null
|
77
|
+
|
78
|
+
echo "add another line --------------------------------------------------------"
|
79
|
+
run-git-crecord " ljjj s"
|
80
|
+
assert-diff "+This is line 1.
|
81
|
+
+This is the second line.
|
82
|
+
+This is line 4."
|
83
|
+
|
84
|
+
|
85
|
+
git ci -a -m "add some lines" > /dev/null
|
86
|
+
|
87
|
+
sed -i '' '1,3d' a_file.txt
|
88
|
+
|
89
|
+
echo "delete all lines --------------------------------------------------------"
|
90
|
+
run-git-crecord "s"
|
91
|
+
assert-diff ""
|
92
|
+
|
93
|
+
git reset > /dev/null
|
94
|
+
|
95
|
+
echo "delete one lines --------------------------------------------------------"
|
96
|
+
run-git-crecord " ljj s"
|
97
|
+
assert-diff "-This is line 1.
|
98
|
+
-This is line 3."
|
99
|
+
|
100
|
+
git reset --hard > /dev/null
|
101
|
+
|
102
|
+
# add some more lines:
|
103
|
+
cat >> a_file.txt << 'EOF'
|
104
|
+
|
105
|
+
This is line 5.
|
106
|
+
This is line 6.
|
107
|
+
This is line 7.
|
108
|
+
This is line 8.
|
109
|
+
This is line 9.
|
110
|
+
|
111
|
+
This is line 10.
|
112
|
+
This is line 11.
|
113
|
+
This is line 12.
|
114
|
+
EOF
|
115
|
+
git ci -a -m "add some more lines" > /dev/null
|
116
|
+
|
117
|
+
sed -i '' '2s/.*/This is line 2./' a_file.txt
|
118
|
+
sed -i '' '12s/.*/This is the tenth line./' a_file.txt
|
119
|
+
sed -i '' '13s/.*/This is the eleventh line./' a_file.txt
|
120
|
+
|
121
|
+
echo "multiple hunks ----------------------------------------------------------"
|
122
|
+
run-git-crecord "s"
|
123
|
+
assert-diff ""
|
124
|
+
|
125
|
+
git reset > /dev/null
|
126
|
+
|
127
|
+
echo "add lines of second hunk ------------------------------------------------"
|
128
|
+
run-git-crecord " ljjj sq "
|
129
|
+
assert-diff "-This is the second line.
|
130
|
+
+This is line 2."
|
131
|
+
|
132
|
+
git reset > /dev/null
|
133
|
+
|
134
|
+
echo "add some lines of all hunks ---------------------------------------------"
|
135
|
+
run-git-crecord " ljj jj jj j s"
|
136
|
+
assert-diff "-This is the second line.
|
137
|
+
-This is line 11."
|
138
|
+
|
139
|
+
git reset > /dev/null
|
140
|
+
|
141
|
+
echo "run git-crecord in a subdirectory directory -----------------------------"
|
142
|
+
mkdir -p "$REPO_DIR"/sub
|
143
|
+
pushd "$REPO_DIR"/sub > /dev/null
|
144
|
+
run-git-crecord "s"
|
145
|
+
assert-diff ""
|
146
|
+
popd > /dev/null # "$REPO_DIR"/sub
|
147
|
+
|
148
|
+
git reset > /dev/null
|
149
|
+
|
150
|
+
echo "add a untracked file ----------------------------------------------------"
|
151
|
+
echo "b_file line 1" > b_file.txt
|
152
|
+
run-git-crecord 'AG s'
|
153
|
+
git commit -m "add b_file" > /dev/null
|
154
|
+
assert-diff '-This is the second line.
|
155
|
+
+This is line 2.
|
156
|
+
-This is line 10.
|
157
|
+
-This is line 11.
|
158
|
+
+This is the tenth line.
|
159
|
+
+This is the eleventh line.'
|
160
|
+
|
161
|
+
echo "a not selected file -----------------------------------------------------"
|
162
|
+
echo "b_file line 2" >> b_file.txt
|
163
|
+
run-git-crecord "j s"
|
164
|
+
assert-diff "+b_file line 2"
|
165
|
+
|
166
|
+
echo "add untracked file from untracked directory -----------------------------"
|
167
|
+
echo "a line" > "$REPO_DIR/sub/sub-file.txt"
|
168
|
+
run-git-crecord "AG s"
|
169
|
+
assert-diff "+b_file line 2"
|
170
|
+
assert-status 'M a_file.txt
|
171
|
+
M b_file.txt
|
172
|
+
A sub/sub-file.txt'
|
173
|
+
|
174
|
+
echo "test with +++ line ------------------------------------------------------"
|
175
|
+
echo "++++" >> b_file.txt
|
176
|
+
run-git-crecord "s"
|
177
|
+
assert-diff ""
|
178
|
+
|
179
|
+
popd > /dev/null # $REPO_DIR
|
180
|
+
|
181
|
+
cat << 'EOF'
|
182
|
+
___ _ __
|
183
|
+
/ _ \| |/ /
|
184
|
+
| | | | ' /
|
185
|
+
| |_| | . \
|
186
|
+
\___/|_|\_\
|
187
|
+
EOF
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: git-crecord
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Maik Brendler
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-06-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '10.1'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 10.1.1
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '10.1'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 10.1.1
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: minitest
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '5.8'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 5.8.4
|
43
|
+
type: :development
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '5.8'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 5.8.4
|
53
|
+
description: This gem adds the git-crecord command. It provides a curses UI to stage/commit
|
54
|
+
git-hunks.
|
55
|
+
email: maik.brendler@invision.de
|
56
|
+
executables:
|
57
|
+
- git-crecord
|
58
|
+
extensions:
|
59
|
+
- ext/mkrf_conf.rb
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- ".rubocop.yml"
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE.txt
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- bin/git-crecord
|
69
|
+
- ext/mkrf_conf.rb
|
70
|
+
- git-crecord.gemspec
|
71
|
+
- lib/git_crecord.rb
|
72
|
+
- lib/git_crecord/diff.rb
|
73
|
+
- lib/git_crecord/diff/difference.rb
|
74
|
+
- lib/git_crecord/diff/file.rb
|
75
|
+
- lib/git_crecord/diff/hunk.rb
|
76
|
+
- lib/git_crecord/diff/line.rb
|
77
|
+
- lib/git_crecord/git.rb
|
78
|
+
- lib/git_crecord/logger.rb
|
79
|
+
- lib/git_crecord/quit_action.rb
|
80
|
+
- lib/git_crecord/ui.rb
|
81
|
+
- lib/git_crecord/ui/color.rb
|
82
|
+
- lib/git_crecord/ui/help_window.rb
|
83
|
+
- lib/git_crecord/ui/hunks_window.rb
|
84
|
+
- lib/git_crecord/version.rb
|
85
|
+
- screenshot.jpg
|
86
|
+
- test/git_crecord/diff/hunk_test.rb
|
87
|
+
- test/system-test.sh
|
88
|
+
- test/test_helper.rb
|
89
|
+
homepage: https://github.com/mbrendler/git-crecord
|
90
|
+
licenses:
|
91
|
+
- MIT
|
92
|
+
metadata:
|
93
|
+
issue_tracker: https://github.com/mbrendler/git-crecord/issues
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options: []
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 2.0.0
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: 1.3.6
|
108
|
+
requirements: []
|
109
|
+
rubyforge_project:
|
110
|
+
rubygems_version: 2.5.1
|
111
|
+
signing_key:
|
112
|
+
specification_version: 4
|
113
|
+
summary: Git command to stage/commit hunks the simple way.
|
114
|
+
test_files:
|
115
|
+
- test/git_crecord/diff/hunk_test.rb
|
116
|
+
- test/system-test.sh
|
117
|
+
- test/test_helper.rb
|