nit 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/README.md +205 -15
  4. data/Rakefile +11 -0
  5. data/Thorfile +19 -0
  6. data/bin/nit +4 -0
  7. data/lib/nit/CHANGES.mk +7 -0
  8. data/lib/nit/app.rb +80 -0
  9. data/lib/nit/commit.rb +7 -0
  10. data/lib/nit/config.rb +81 -0
  11. data/lib/nit/files.rb +60 -0
  12. data/lib/nit/ignore.rb +36 -0
  13. data/lib/nit/lines.rb +63 -0
  14. data/lib/nit/status.rb +94 -0
  15. data/lib/nit/version.rb +1 -1
  16. data/lib/nit.rb +0 -3
  17. data/nit.gemspec +6 -3
  18. data/test/config_test.rb +15 -0
  19. data/test/dummies/stage/.gitignore +1 -0
  20. data/test/dummies/stage/brandnew.rb +0 -0
  21. data/test/dummies/stage/git/COMMIT_EDITMSG +21 -0
  22. data/test/dummies/stage/git/HEAD +1 -0
  23. data/test/dummies/stage/git/config +5 -0
  24. data/test/dummies/stage/git/description +1 -0
  25. data/test/dummies/stage/git/hooks/applypatch-msg.sample +15 -0
  26. data/test/dummies/stage/git/hooks/commit-msg.sample +24 -0
  27. data/test/dummies/stage/git/hooks/post-update.sample +8 -0
  28. data/test/dummies/stage/git/hooks/pre-applypatch.sample +14 -0
  29. data/test/dummies/stage/git/hooks/pre-commit.sample +50 -0
  30. data/test/dummies/stage/git/hooks/pre-rebase.sample +169 -0
  31. data/test/dummies/stage/git/hooks/prepare-commit-msg.sample +36 -0
  32. data/test/dummies/stage/git/hooks/update.sample +128 -0
  33. data/test/dummies/stage/git/index +0 -0
  34. data/test/dummies/stage/git/info/exclude +6 -0
  35. data/test/dummies/stage/git/logs/HEAD +2 -0
  36. data/test/dummies/stage/git/logs/refs/heads/master +2 -0
  37. data/test/dummies/stage/git/objects/0f/e4f83176db6842cd95f439f1edad3cb8483b4e +0 -0
  38. data/test/dummies/stage/git/objects/5c/0d92dd14afeb4c2bcd11bc3f072390be598a73 +1 -0
  39. data/test/dummies/stage/git/objects/60/bd895ee4a908b754f1980491c0e0852c29a8cd +0 -0
  40. data/test/dummies/stage/git/objects/b7/5ac79d08245a3a8a662520dd90f80be097cc44 +1 -0
  41. data/test/dummies/stage/git/objects/c1/172bf75d73a630c467f1e1448c44d9188fe23e +0 -0
  42. data/test/dummies/stage/git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
  43. data/test/dummies/stage/git/refs/heads/master +1 -0
  44. data/test/dummies/stage/new.rb +0 -0
  45. data/test/dummies/stage/on_stage.rb +1 -0
  46. data/test/dummies/stage/staged.rb +1 -0
  47. data/test/files_test.rb +45 -0
  48. data/test/ignore_test.rb +46 -0
  49. data/test/lines_test.rb +26 -0
  50. data/test/nit_test.rb +48 -0
  51. data/test/status_test.rb +88 -0
  52. data/test/test_helper.rb +2 -0
  53. metadata +122 -19
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 553741b0650ad5eba01775f8cda663d3aedf8ca0
4
+ data.tar.gz: 9041785b1191731540185990574b2dc3bb3978a9
5
+ SHA512:
6
+ metadata.gz: ff8ad770fa0d4022b6712c890128d420e23bb0cefa50445e77a3bf9b895178822e308433ba955de777925d98f475e059e0b26944dff9f7d26074ae60855696d7
7
+ data.tar.gz: 0aa20962038881633ce12558f12a0025b5377ed313fa539ca59992a8105320239ffa427730e8e587d3b067847556fdf4735d30aac1a052fc16fc8827527a64e1
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ .nit
data/README.md CHANGED
@@ -1,29 +1,219 @@
1
1
  # Nit
2
2
 
3
- TODO: Write a gem description
3
+ _Improving your Git workflow since 2013._
4
+
5
+ Nit is a thin command-line wrapper for git. It gives you handy shortcuts for your everyday work with git, reduces your typing and is easily extendable with your own commands.
6
+
4
7
 
5
8
  ## Installation
6
9
 
7
- Add this line to your application's Gemfile:
10
+ ```
11
+ gem install nit
12
+ ```
13
+
14
+ This will install the `nit` shell command.
15
+
16
+
17
+ ## Status
18
+
19
+ The blank `nit` command will be your best friend.
20
+
21
+ ```shell
22
+ nit
23
+ ```
24
+
25
+ It's a `git status` on LSD.
26
+
27
+ ```shell
28
+ # On branch master
29
+ # Changes not staged for commit:
30
+ # (use "git add <file>..." to update what will be committed)
31
+ # (use "git checkout -- <file>..." to discard changes in working directory)
32
+ #
33
+ # modified: on_stage.rb [a]
34
+ # modified: staged.rb [b]
35
+ #
36
+ # Untracked files:
37
+ # (use "git add <file>..." to include in what will be committed)
38
+ #
39
+ # brandnew.rb [c]
40
+ # new.rb [d]
41
+ ```
42
+
43
+ First of all, it shows you the current branch in bold.
44
+
45
+ ## File Indexes
46
+
47
+ Secondly, the status screen renders a file index for each file which can be used on the command-line.
48
+
49
+ Indexes per default are characters on the right-hand side of the filename. This can be configured, in case you prefer digits. You can also have the indexes prepended to the filename.
50
+
51
+ However, scientific studies with `nit` over the last 18 years have proven that characters instead of digits are faster to reach when typing. Also, indexes _appended_ to the filename make them easier to read when deciding what to commit.
52
+
53
+ ## Commit
54
+
55
+ To commit files, you no longer use the filename but their indexes.
56
+
57
+ ```shell
58
+ nit commit a c
59
+ ```
60
+
61
+ Which will run
62
+
63
+ ```
64
+ git add on_stage.rb
65
+ git add brandnew.rb
66
+ git commit
67
+ ```
68
+
69
+ Note that nits adds and commits an already staged file _and_ an untracked file in the same step.
70
+
71
+ This could also be run as
72
+
73
+ ```shell
74
+ nit commit ac
75
+ ```
76
+
77
+ Nit will extract the correct indexes.
78
+
79
+
80
+ ## Auto-Expansion Of Commands
81
+
82
+ You don't have to type `nit commit` everytime. Use a short-cut, nit figures out what command you want. This command will work, too.
83
+
84
+ ```shell
85
+ nit co abc
86
+ ```
87
+
88
+ ## Add
89
+
90
+ Adding with nit becomes obsolete. It is handled by the `commit` command.
91
+
92
+
93
+ ## Ignoring Files
94
+
95
+ When files on the status screen get in your way you can _ignore_ them. Nit will simply not consider them anymore until you `unignore` them.
96
+
97
+ Ignoring files is roughly equivalent to `git stash`. However, instead of having to remember dozens of stashes, you simply tell nit to hide files from your status. That helps focusing on the files you are actually working on. Trust us.
98
+
99
+
100
+ ```shell
101
+ nit ignore a d
102
+ ```
103
+
104
+ This tells nit to ignore `on_stage.rb` and `new.rb`.
105
+
106
+ ```shell
107
+ $ nit
108
+
109
+ # On branch master
110
+ # Changes not staged for commit:
111
+ # (use "git add <file>..." to update what will be committed)
112
+ # (use "git checkout -- <file>..." to discard changes in working directory)
113
+ #
114
+ # modified: staged.rb [a]
115
+ #
116
+ # Untracked files:
117
+ # (use "git add <file>..." to include in what will be committed)
118
+ #
119
+ # brandnew.rb [b]
120
+ #
121
+ # Ignored files: 2
122
+ ```
123
+
124
+ The ignored files are now longer visible. However, the last line reminds you that there are files ignored.
125
+
126
+ To get a list of ignored files, run:
127
+
128
+ ```shell
129
+ nit ignore
130
+ ```
131
+
132
+ ```shell
133
+ [a] on_stage.rb
134
+ [b] new.rb
135
+ ```
136
+
137
+ If you want to commit to the ignored files, just unignore them, again.
138
+
139
+ ```shell
140
+ nit unignore a
141
+ ```
142
+
143
+ ## Push
144
+
145
+ To push your changes, run the following command.
146
+
147
+ ```shell
148
+ nit push
149
+ ```
150
+
151
+ Will figure out the current branch and run
152
+
153
+ ```shell
154
+ git push origin <current branch>
155
+ ```
156
+
157
+ ## Pull
158
+
159
+ The same works for `nit pull`.
160
+
161
+
162
+ ## Arbitrary Commands
163
+
164
+ You can use nit's index interpolation for any git command.
165
+
166
+ ```shell
167
+ nit diff ab
168
+ ``
169
+
170
+
171
+ ## Manual Configuration
172
+
173
+ Some operations require nit to save application state. This is done in the `.nit` YAML file in your working directory. You can manually edit it.
174
+
175
+
176
+ ## Alternative Indexing
177
+
178
+ You can have number-based indexing.
179
+
180
+ ```shell
181
+ modified: on_stage.rb [0]
182
+ modified: staged.rb [1]
183
+ ```
184
+
185
+ In `.nit`, do
186
+
187
+ ```yaml
188
+ indexer = "IntegerIndexer"
189
+ ```
190
+
191
+ Some people prefer the index prepending the file name.
192
+
193
+ ```shell
194
+ modified: [0] on_stage.rb
195
+ modified: [1] staged.rb
196
+
197
+ ```
198
+
199
+ Nap in the park.
200
+
201
+ ```yaml
202
+ index_renderer = "PrependIndexRenderer"
203
+ ```
8
204
 
9
- gem 'nit'
205
+ ## Extending Nit
10
206
 
11
- And then execute:
207
+ Extending nit with your own commands can be done either with gems or by placing the extensions into your `.nit` directory. This will be implemented and documented soon.
12
208
 
13
- $ bundle
14
209
 
15
- Or install it yourself as:
210
+ ## More
16
211
 
17
- $ gem install nit
212
+ There are plenty of more features planned for the upcoming versions.
18
213
 
19
- ## Usage
20
214
 
21
- TODO: Write usage instructions here
215
+ ## License
22
216
 
23
- ## Contributing
217
+ Copyright (c) 2013, Nick Sutterer <apotonick@gmail.com>
24
218
 
25
- 1. Fork it
26
- 2. Create your feature branch (`git checkout -b my-new-feature`)
27
- 3. Commit your changes (`git commit -am 'Add some feature'`)
28
- 4. Push to the branch (`git push origin my-new-feature`)
29
- 5. Create new Pull Request
219
+ Released under the MIT License.
data/Rakefile CHANGED
@@ -1 +1,12 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+
5
+ desc 'Test the nit gem.'
6
+ task :default => :test
7
+
8
+ Rake::TestTask.new(:test) do |test|
9
+ test.libs << 'test'
10
+ test.test_files = FileList["test/status_test.rb"] + FileList['test/*_test.rb']
11
+ test.verbose = true
12
+ end
data/Thorfile ADDED
@@ -0,0 +1,19 @@
1
+ $:.unshift File.expand_path("../lib", __FILE__)
2
+
3
+ require 'bundler'
4
+ require 'thor/rake_compat'
5
+
6
+ class Default < Thor
7
+ include Thor::RakeCompat
8
+ Bundler::GemHelper.install_tasks
9
+
10
+ desc "build", "Build nit-#{Nit::VERSION}.gem into the pkg directory"
11
+ def build
12
+ Rake::Task["build"].execute
13
+ end
14
+
15
+ desc "install", "Build and install nit-#{Nit::VERSION}.gem into system gems"
16
+ def install
17
+ Rake::Task["install"].execute
18
+ end
19
+ end
data/bin/nit ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "nit/app"
4
+ Nit::App.start
@@ -0,0 +1,7 @@
1
+ h2. 0.0.2
2
+
3
+ * First real release.
4
+
5
+ h2. 0.0.1
6
+
7
+ * Claiming the `nit` gem name.
data/lib/nit/app.rb ADDED
@@ -0,0 +1,80 @@
1
+ require "thor"
2
+ require "nit/files"
3
+ require "nit/lines"
4
+ require "nit/status"
5
+ require "nit/commit"
6
+ require "nit/config"
7
+ require "nit/ignore"
8
+
9
+
10
+
11
+ # TODO:
12
+ # * diff workflow: going through changed files, adding them while reading!
13
+ # * nit unstage => git reset HEAD
14
+ # * nit ignore (save time and last changed!)
15
+ # * nit unignore
16
+ # * nit co -a => git commit -a
17
+ # * nit co 1<tab> => filename
18
+
19
+ module Nit
20
+ class App < Thor
21
+ include Actions
22
+
23
+ default_command(:status)
24
+
25
+ desc "status", "bla"
26
+ def status
27
+ puts Status.new(config).call
28
+ end
29
+
30
+ desc "commit", "blubb"
31
+ def commit(*args)
32
+ puts Commit.new(config).call(`git status`, args)
33
+ end
34
+
35
+ desc "ignore", "blubb"
36
+ def ignore(*args)
37
+ puts Ignore.new(config).call(`git status`, args)
38
+ end
39
+
40
+ desc "unignore", "blubb"
41
+ def unignore(*args)
42
+ puts Unignore.new(config).call(`git status`, args)
43
+ end
44
+
45
+
46
+ desc "pull", "pull from current branch at origin"
47
+ def pull
48
+ `git pull origin #{current_branch}`
49
+ end
50
+
51
+ desc "push", "push to current branch at origin"
52
+ def push
53
+ `git push origin #{current_branch}`
54
+ end
55
+
56
+ private
57
+ def current_branch
58
+ output = `git branch`
59
+ branch = output.match(/\* (.+)/)[1].strip
60
+ end
61
+
62
+ def config
63
+ @config ||= Config.new # TODO: eventually pass path.
64
+ end
65
+
66
+ # def self.dynamic_command_class
67
+ # DynamicCommand
68
+ # end
69
+
70
+ #class DynamicCommand < Thor::DynamicCommand
71
+ Thor::DynamicCommand.class_eval do # see https://github.com/erikhuda/thor/pull/358
72
+ def run(app, indexes)
73
+ command = self.name
74
+ state = Status::State.new(`git status`, app.send(:config))
75
+
76
+ puts `git #{command} #{state.files.list(indexes)}`
77
+ end
78
+ end
79
+ end
80
+ end
data/lib/nit/commit.rb ADDED
@@ -0,0 +1,7 @@
1
+ module Nit
2
+ class Commit < Status
3
+ def process(state, indexes)
4
+ system "git add #{state.files.list(indexes)} && git commit"
5
+ end
6
+ end
7
+ end
data/lib/nit/config.rb ADDED
@@ -0,0 +1,81 @@
1
+ require "psych"
2
+ require "thor"
3
+
4
+ module Nit
5
+ class Config < Thor::Group # we have to derive from Group to get the create_file working - that's messy.
6
+ include Thor::Actions
7
+ # Warning: this API will soon change.
8
+
9
+ class File
10
+ def initialize(name, app) # FIXME: i hate the app dependency for #create_file.
11
+ @name = name
12
+ @app = app
13
+ end
14
+
15
+ def read(key)
16
+ return unless exist?
17
+ yaml_hash[key]
18
+ end
19
+
20
+ def write(key, value)
21
+ @app.create_file(@name, Psych.dump({})) unless exist?
22
+
23
+ hash = yaml_hash
24
+ hash[key] = value
25
+ ::File.open(@name, "w") { |f| f.write(Psych.dump(hash)) }
26
+ end
27
+
28
+ def rm!
29
+ @app.remove_file(@name)
30
+ end
31
+
32
+ private
33
+ def exist?
34
+ ::File.exist?(@name)
35
+ end
36
+
37
+ def yaml_hash
38
+ Psych.load_file(@name) || {}
39
+ end
40
+ end
41
+
42
+
43
+ def initialize
44
+ super
45
+ @file = File.new(filename, self) # FIXME: remove the Thor::App dependency, see above!
46
+ end
47
+
48
+ def ignored_files
49
+ file.read("ignored_files") or []
50
+ end
51
+
52
+ def ignored_files=(files)
53
+ file.write("ignored_files", files.collect(&:to_s))
54
+ end
55
+
56
+ def indexer
57
+ const = file.read("indexer") || "CharIndexer"
58
+ Nit::Files.const_get(const)
59
+ end
60
+
61
+ def indexer=(value)
62
+ file.write("indexer", value)
63
+ end
64
+
65
+ def index_renderer
66
+ const = file.read("index_renderer") || "AppendIndexRenderer"
67
+ Nit::Status.const_get(const)
68
+ end
69
+
70
+ def index_renderer=(value)
71
+ file.write("index_renderer", value)
72
+ end
73
+
74
+ private
75
+ attr_reader :file
76
+
77
+ def filename
78
+ ".nit"
79
+ end
80
+ end
81
+ end
data/lib/nit/files.rb ADDED
@@ -0,0 +1,60 @@
1
+ module Nit
2
+ class Files < Array
3
+ def initialize(arr=[], indexer=IntegerIndexer)
4
+ super(arr)
5
+ extend indexer
6
+ end
7
+
8
+ # we could also use a dedicated object here.
9
+ module PublicMethods
10
+ def [](index)
11
+ super(index.to_i)
12
+ end
13
+
14
+ # Return list of file names for indexes.
15
+ def evaluate(indexes)
16
+ indexes.collect { |i| self[i] }
17
+ end
18
+
19
+ # decorator:
20
+ def list(indexes)
21
+ indexes.collect { |i| self[i] }.join(" ")
22
+ end
23
+ end
24
+ include PublicMethods
25
+
26
+ private
27
+
28
+ module IntegerIndexer
29
+ end
30
+
31
+ module CharIndexer
32
+ def [](char)
33
+ index = map.index(char)
34
+ super(index)
35
+ end
36
+
37
+ def evaluate(chars)
38
+ super(split(chars)) # "nit commit abc"
39
+ end
40
+
41
+ def index(file)
42
+ map[super]
43
+ end
44
+
45
+ def list(indexes)
46
+ super(split(indexes))
47
+ end
48
+
49
+ private
50
+ def map
51
+ ("a".."z").collect(&:to_s)
52
+ end
53
+
54
+ def split(chars)
55
+ return chars unless chars.find { |c| c.length > 1 } # DISCUSS: what if "> aa bcd xx" where xx is actually an index?
56
+ chars.first.split("")
57
+ end
58
+ end
59
+ end
60
+ end
data/lib/nit/ignore.rb ADDED
@@ -0,0 +1,36 @@
1
+ module Nit
2
+ class Ignore < Status
3
+ def initialize(*)
4
+ super
5
+ @ignores = Files.new(@config.ignored_files)
6
+ end
7
+ attr_reader :ignores
8
+
9
+ private
10
+ def process(state, indexes)
11
+ return show if indexes.size == 0
12
+
13
+ file_list = state.files.evaluate(indexes).compact
14
+
15
+ @config.ignored_files=(@config.ignored_files + file_list)
16
+ end
17
+
18
+ def show
19
+ return if ignores.size == 0
20
+
21
+ output = "Ignored files:\n"
22
+ ignores.each { |f| output << "[#{ignores.index(f)}] #{f}\n" }
23
+
24
+ output
25
+ end
26
+ end
27
+
28
+ class Unignore < Ignore
29
+ private
30
+ def process(state, indexes)
31
+ file_list = state.ignored.evaluate(indexes).compact
32
+
33
+ @config.ignored_files=(@config.ignored_files - file_list.map(&:to_s)) # FIXME: make this work with strings and File instances.
34
+ end
35
+ end
36
+ end
data/lib/nit/lines.rb ADDED
@@ -0,0 +1,63 @@
1
+ module Nit
2
+ class Lines < Array
3
+ def initialize(text)
4
+ super(text.split("\n").collect { |ln| Line.new(ln, self) })
5
+ end
6
+
7
+ def find(pattern)
8
+ each do |ln|
9
+ next unless matches = ln.match(pattern)
10
+
11
+ yield ln, matches
12
+ end
13
+ end
14
+
15
+ def files
16
+ files = []
17
+
18
+ for type, pattern in file_patterns
19
+ find(pattern) do |ln, matches|
20
+ files << file = File.new(matches[1].strip, ln)
21
+ end
22
+ end
23
+
24
+ files
25
+ end
26
+
27
+ def to_s
28
+ join("\n")
29
+ end
30
+
31
+ #private
32
+ def file_patterns
33
+ {
34
+ modified: /#\tmodified:(.+)/,
35
+ new: /#\t([^modified:].+)/
36
+ }
37
+ end
38
+ end
39
+
40
+ class Line < String
41
+ def initialize(string, screen)
42
+ super(string)
43
+ @screen = screen
44
+ end
45
+
46
+ # Deletes the entire line from the screen.
47
+ def delete
48
+ @screen.delete(self)
49
+ end
50
+ end
51
+
52
+ class File
53
+ def initialize(path, line)
54
+ @path, @line = path, line
55
+ end
56
+
57
+ attr_reader :path, :line
58
+
59
+ def to_s
60
+ path.to_s
61
+ end
62
+ end
63
+ end