git-ui 0.0.1

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.
Files changed (7) hide show
  1. data/Rakefile +51 -0
  2. data/bin/git-i +30 -0
  3. data/doc/LICENSE +19 -0
  4. data/doc/README +0 -0
  5. data/doc/TODO +0 -0
  6. data/lib/git_ui.rb +205 -0
  7. metadata +79 -0
@@ -0,0 +1,51 @@
1
+ require 'rake/gempackagetask'
2
+ require 'rake/rdoctask'
3
+
4
+ $: << "#{File.dirname(__FILE__)}/lib"
5
+
6
+ spec = Gem::Specification.new { |s|
7
+ s.platform = Gem::Platform::RUBY
8
+
9
+ s.author = "Pete Elmore"
10
+ s.email = "1337p337@gmail.com"
11
+ s.files = Dir["{lib,doc,bin,ext}/**/*"].delete_if {|f|
12
+ /\/rdoc(\/|$)/i.match f
13
+ } + %w(Rakefile)
14
+ s.require_path = 'lib'
15
+ s.has_rdoc = true
16
+ s.extra_rdoc_files = Dir['doc/*'].select(&File.method(:file?))
17
+ s.extensions << 'ext/extconf.rb' if File.exist? 'ext/extconf.rb'
18
+ Dir['bin/*'].map(&File.method(:basename)).map(&s.executables.method(:<<))
19
+
20
+ s.name = 'git-ui'
21
+ s.summary = 'Hacky attempt at a workable UI for git, modeled slightly on '\
22
+ 'darcs.'
23
+ s.homepage = "http://debu.gs/#{s.name}"
24
+ %w(colorize escape).each &s.method(:add_dependency)
25
+ s.version = '0.0.1'
26
+ }
27
+
28
+ Rake::RDocTask.new(:doc) { |t|
29
+ t.main = 'doc/README'
30
+ t.rdoc_files.include 'lib/**/*.rb', 'doc/*', 'bin/*', 'ext/**/*.c',
31
+ 'ext/**/*.rb'
32
+ t.options << '-S' << '-N'
33
+ t.rdoc_dir = 'doc/rdoc'
34
+ }
35
+
36
+ Rake::GemPackageTask.new(spec) { |pkg|
37
+ pkg.need_tar_bz2 = true
38
+ }
39
+ desc "Cleans out the packaged files."
40
+ task(:clean) {
41
+ FileUtils.rm_rf 'pkg'
42
+ }
43
+
44
+ task(:install => :package) {
45
+ g = "pkg/#{spec.name}-#{spec.version}.gem"
46
+ system "sudo gem install -l #{g}"
47
+ }
48
+
49
+ task(:irb) {
50
+ exec "irb -Ilib -r#{spec.name}"
51
+ }
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'git_ui'
4
+ include GitUI
5
+
6
+ args = ARGV.dup
7
+
8
+ if args.empty? || %w(-h --help -help help).any?(&args.method(:include?))
9
+ puts "Available commands:",
10
+ "add: Add files to the repository",
11
+ "record: Record changes (stages and commits) interactively"
12
+ exit 0
13
+ end
14
+
15
+ cmd = determine_cmd args
16
+
17
+ if cmd.nil?
18
+ $stderr.puts "Command doesn't make sense: #{args}"
19
+ exit 1
20
+ end
21
+
22
+ # git usually gives paths relative to the repo's root, but sometimes requires
23
+ # paths relative to the working directory.
24
+ dotgit = GitUI.find_dotgit
25
+ if File.expand_path('.') != dotgit
26
+ puts "Looks like the root of this repo is #{dotgit}..."
27
+ Dir.chdir dotgit
28
+ end
29
+
30
+ cmd[]
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Peter Elmore (pete.elmore at gmail.com)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a
4
+ copy of this software and associated documentation files (the "Software"),
5
+ to deal in the Software without restriction, including without limitation
6
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
+ and/or sell copies of the Software, and to permit persons to whom the
8
+ Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19
+ DEALINGS IN THE SOFTWARE.
File without changes
File without changes
@@ -0,0 +1,205 @@
1
+ require 'rubygems'
2
+ %w(colorize escape).each &method(:require)
3
+
4
+ module GitUI
5
+ class Action
6
+ attr_accessor :key, :desc, :block
7
+ def initialize(k, d, &b)
8
+ @key = k
9
+ @desc = d
10
+ @block = b
11
+ end
12
+ end
13
+
14
+ def act *a, &b
15
+ Action.new(*a, &b)
16
+ end
17
+
18
+ def compose *lambdas
19
+ lambda { |*args|
20
+ lambdas.reverse.inject(args) { |acc, f| f.call *acc }
21
+ }
22
+ end
23
+
24
+ def pager
25
+ @pager ||= %w(GIT_UI_PAGER GIT_PAGER PAGER
26
+ ).map { |e| ENV[e] }.compact.first || 'less'
27
+ end
28
+
29
+ def editor
30
+ @editor ||= %w(GIT_UI_EDITOR GIT_EDITOR VISUAL EDITOR
31
+ ).map { |e| ENV[e] }.compact.first || 'vi'
32
+ end
33
+
34
+ def find_dotgit(path = '.')
35
+ path = original_path = File.expand_path(path)
36
+ loop {
37
+ return path if File.directory?(File.join(path, '.git'))
38
+ p = File.expand_path File.join(path, '..')
39
+ if p == path
40
+ raise RuntimeError, "#{original_path} does not appear " \
41
+ "to be inside a git repository."
42
+ end
43
+ path = p
44
+ }
45
+ end
46
+
47
+ def changes
48
+ IO.popen('git diff --raw', 'r').readlines.grep(/^:/).map { |l|
49
+ src_mode, dst_mode, src_sha1, dst_sha1, status, src_path, dst_path =
50
+ l.sub(/^:/, '').split(/\s+/)
51
+ { :status => status,
52
+ :filename => (dst_path || src_path).chomp,
53
+ }
54
+ }
55
+ end
56
+
57
+ def untracked_files
58
+ IO.popen('git ls-files -o --exclude-standard', 'r').readlines.map { |f|
59
+ { :filename => f.chomp }
60
+ }
61
+ end
62
+
63
+ def add_untracked
64
+ cs = untracked_files
65
+ color = :yellow
66
+
67
+ if cs.empty?
68
+ puts "No new files; try your .gitignore, maybe?".colorize(color)
69
+ return []
70
+ end
71
+
72
+ i = 0
73
+ acts = [
74
+ act('y', "Yes, add this file.") {
75
+ cs[i][:action] = :add; i += 1 },
76
+ act('n', 'No, don\'t add this file.') {
77
+ cs[i][:action] = nil; i += 1 },
78
+ act('v', 'View the file') {
79
+ print File.read(cs[i][:filename]) },
80
+ act('p', "View this file in a pager (#{pager})") {
81
+ system "#{pager} #{cs[i][:filename]}" },
82
+ act('e', "Edit this file using \"#{editor}\"") {
83
+ edit cs[i][:filename] },
84
+ act('d', 'Done, skip to commit step.') { i = cs.size },
85
+ act('a', 'Add all remaining') {
86
+ cs[i..-1].each { |c| c[:action] = :add }
87
+ i = cs.size },
88
+ act('q', 'Quit, skip commit step.') { cs.clear },
89
+ act('j', 'Jump to next file in list') { i += 1 },
90
+ act('k', 'Go back to previous file in list') {
91
+ i -= 1; i = 0 if i < 0 },
92
+ ]
93
+
94
+ while i < cs.size
95
+ puts "#{cs[i][:filename]} (#{i + 1}/#{cs.size})"
96
+ decide "Add?", acts, color
97
+ end
98
+
99
+ cs
100
+ end
101
+
102
+ def decide prompt, acts, color = :default
103
+ keys = acts.map { |act| act.key } << '?'
104
+ prompt = "#{prompt} [#{keys.join('')}]: ".colorize(color)
105
+
106
+ act = nil
107
+ loop {
108
+ print prompt
109
+ $stdout.flush
110
+ inp = $stdin.read(1)
111
+ print "\n"
112
+ if inp == '?'
113
+ puts acts.map { |a|
114
+ " #{a.key}: #{a.desc}"
115
+ }
116
+ elsif a = acts.find { |a| inp == a.key }
117
+ return a.block.call
118
+ else
119
+ puts "No such action: #{inp}"
120
+ end
121
+ }
122
+ end
123
+
124
+ def edit filename
125
+ system "#{editor} #{Escape.shell_single_word filename}"
126
+ end
127
+
128
+ def record *args
129
+ cs = changes
130
+ if cs.empty?
131
+ puts "No changes to record!".colorize(:cyan)
132
+ return []
133
+ end
134
+ i = 0
135
+
136
+ acts = [
137
+ act('y', "Yes, commit this change.") {
138
+ cs[i][:action] = :commit; i += 1 },
139
+ act('n', 'No, don\'t commit this change.') {
140
+ cs[i][:action] = nil; i += 1 },
141
+ act('v', 'View this patch') {
142
+ system "git diff #{cs[i][:filename]}" },
143
+ act('p', "View this patch in a pager (#{pager})") {
144
+ system "git diff #{cs[i][:filename]} | #{pager}" },
145
+ act('e', "Edit this file using \"#{editor}\"") {
146
+ edit cs[i][:filename] },
147
+ act('d', 'Done, skip to commit step.') { i = cs.size },
148
+ act('a', 'Record all remaining') {
149
+ cs[i..-1].each { |c| c[:action] = :record }
150
+ i = cs.size },
151
+ act('q', 'Quit, skip commit step.') { cs.clear },
152
+ act('j', 'Jump to next patch in list') { i += 1 },
153
+ act('k', 'Go back to previous patch in list') {
154
+ i -= 1; i = 0 if i < 0 },
155
+ ]
156
+
157
+ while i < cs.size
158
+ puts "#{cs[i][:filename]} #{cs[i][:status]} (#{i + 1}/#{cs.size})"
159
+ decide "Record?", acts, :cyan
160
+ end
161
+
162
+ cs
163
+ end
164
+
165
+ def commit(*cs)
166
+ fs = cs.select { |c|
167
+ [:commit, :add].include? c[:action]
168
+ }.map { |c| c[:filename] }
169
+ if fs.empty?
170
+ puts "Nothing to commit.".colorize(:red)
171
+ return
172
+ end
173
+ puts "Committing #{fs.join(', ')}...".colorize(:green)
174
+ system 'git', 'add', *fs
175
+ system 'git', 'commit', *fs
176
+ end
177
+
178
+ def determine_cmd args
179
+ stty_cooked = lambda { |*args| system "stty sane"; args }
180
+ stty_raw = lambda { |*args| system "stty -icanon min 1"; args }
181
+
182
+ cmd, *args = args
183
+
184
+ case cmd
185
+ when /^rec/i
186
+ lambda {
187
+ compose(method(:commit),
188
+ stty_cooked,
189
+ method(:record),
190
+ stty_raw)[*args]
191
+ }
192
+ when /^add/i
193
+ lambda {
194
+ compose(method(:commit),
195
+ stty_cooked,
196
+ method(:add_untracked),
197
+ stty_raw)[*args]
198
+ }
199
+ else
200
+ nil
201
+ end
202
+ end
203
+
204
+ extend self
205
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: git-ui
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Pete Elmore
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-28 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: colorize
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: escape
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ description:
36
+ email: 1337p337@gmail.com
37
+ executables:
38
+ - git-i
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - doc/LICENSE
43
+ - doc/TODO
44
+ - doc/README
45
+ files:
46
+ - lib/git_ui.rb
47
+ - doc/LICENSE
48
+ - doc/TODO
49
+ - doc/README
50
+ - bin/git-i
51
+ - Rakefile
52
+ has_rdoc: true
53
+ homepage: http://debu.gs/git-ui
54
+ post_install_message:
55
+ rdoc_options: []
56
+
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "0"
70
+ version:
71
+ requirements: []
72
+
73
+ rubyforge_project:
74
+ rubygems_version: 1.3.1
75
+ signing_key:
76
+ specification_version: 2
77
+ summary: Hacky attempt at a workable UI for git, modeled slightly on darcs.
78
+ test_files: []
79
+