gitXplorer 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/bin/gitX +137 -0
- data/bin/gitXplorer +137 -0
- data/bin/gitx +137 -0
- data/bin/gitxplorer +137 -0
- data/lib/git_xplorer/error/invalid_revision.rb +5 -0
- data/lib/git_xplorer/error/no_chaining_allowed.rb +5 -0
- data/lib/git_xplorer/error/no_file_or_directory.rb +5 -0
- data/lib/git_xplorer/error/no_semicolons_allowed.rb +5 -0
- data/lib/git_xplorer/error/no_subshells_allowed.rb +5 -0
- data/lib/git_xplorer/error/not_directory.rb +5 -0
- data/lib/git_xplorer/error/not_file.rb +5 -0
- data/lib/git_xplorer/error.rb +10 -0
- data/lib/git_xplorer/git_object/directory.rb +67 -0
- data/lib/git_xplorer/git_object/file.rb +48 -0
- data/lib/git_xplorer/git_object/revision.rb +21 -0
- data/lib/git_xplorer/git_object.rb +112 -0
- data/lib/git_xplorer/wish/cat_wish.rb +40 -0
- data/lib/git_xplorer/wish/change_directory_wish.rb +44 -0
- data/lib/git_xplorer/wish/find_wish.rb +28 -0
- data/lib/git_xplorer/wish/git_wish.rb +30 -0
- data/lib/git_xplorer/wish/grep_wish.rb +45 -0
- data/lib/git_xplorer/wish/list_directory_wish.rb +93 -0
- data/lib/git_xplorer/wish/pwd_wish.rb +24 -0
- data/lib/git_xplorer/wish/vi_wish.rb +46 -0
- data/lib/git_xplorer.rb +219 -0
- metadata +152 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
class GitXplorer::GitObject::File < GitXplorer::GitObject
|
2
|
+
def children
|
3
|
+
if (@kids.nil?)
|
4
|
+
# Initialize
|
5
|
+
@kids = Array.new
|
6
|
+
|
7
|
+
# Create git command
|
8
|
+
cmd = [
|
9
|
+
"git log --date=format-local:\"%F_%I:%M:%S_%P\"",
|
10
|
+
"--diff-filter=AC",
|
11
|
+
"--full-history",
|
12
|
+
"--pretty=tformat:\"{{%H}}{{%s}}{{%cd}}{{%an}}\"",
|
13
|
+
"--",
|
14
|
+
absolute_path
|
15
|
+
].join(" ")
|
16
|
+
|
17
|
+
# Loop thru results creating a revision object for each
|
18
|
+
%x(#{cmd}).split("\n").each do |line|
|
19
|
+
line.match(/{{(.+)}}{{(.+)}}{{(.+)}}{{(.+)}}/) do |m|
|
20
|
+
@kids.push(
|
21
|
+
GitXplorer::GitObject::Revision.new(
|
22
|
+
m[1],
|
23
|
+
self,
|
24
|
+
m[2],
|
25
|
+
m[3],
|
26
|
+
m[4]
|
27
|
+
)
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
return @kids
|
34
|
+
end
|
35
|
+
|
36
|
+
def desc
|
37
|
+
return "#{newest.name} #{newest.date}" if (newest)
|
38
|
+
return nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize(name, parent)
|
42
|
+
super(name, parent)
|
43
|
+
end
|
44
|
+
|
45
|
+
def newest
|
46
|
+
return children[0]
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class GitXplorer::GitObject::Revision < GitXplorer::GitObject
|
2
|
+
attr_reader :date
|
3
|
+
attr_reader :summary
|
4
|
+
attr_reader :user
|
5
|
+
|
6
|
+
def desc
|
7
|
+
return "#{@date} #{@user} #{@summary}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(revision, parent, summary, date, user)
|
11
|
+
super(revision, parent)
|
12
|
+
@date = date
|
13
|
+
@summary = summary
|
14
|
+
@user = user
|
15
|
+
end
|
16
|
+
|
17
|
+
def tab_complete(color = false)
|
18
|
+
color ||= false
|
19
|
+
return {@name => @date}
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
class GitXplorer::GitObject
|
2
|
+
attr_reader :name
|
3
|
+
attr_reader :parent
|
4
|
+
|
5
|
+
def absolute_path
|
6
|
+
return "" if (@parent.nil?)
|
7
|
+
return [@parent.absolute_path, @name].join
|
8
|
+
end
|
9
|
+
|
10
|
+
def children
|
11
|
+
@kids ||= Array.new
|
12
|
+
return @kids
|
13
|
+
end
|
14
|
+
|
15
|
+
def desc
|
16
|
+
return ""
|
17
|
+
end
|
18
|
+
|
19
|
+
def exist?(path)
|
20
|
+
return !get(path).nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
def get(path)
|
24
|
+
# Return self if path is nil or empty
|
25
|
+
return self if (path.nil? || path.empty?)
|
26
|
+
|
27
|
+
# Split path on /
|
28
|
+
child, recurse, grandchild = path.partition("/")
|
29
|
+
child, _, grandchild = path.partition(":") if (recurse.empty?)
|
30
|
+
|
31
|
+
case child
|
32
|
+
when "."
|
33
|
+
# Same directory
|
34
|
+
return self.get(grandchild)
|
35
|
+
when ".."
|
36
|
+
# Check parent if it exists
|
37
|
+
return @parent.get(grandchild) if (@parent)
|
38
|
+
|
39
|
+
# Otherwise we reached top-level so just keep going
|
40
|
+
return self.get(grandchild)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Check child if it exists
|
44
|
+
if (has_child?(child))
|
45
|
+
kids = children.select do |kid|
|
46
|
+
kid.name == child
|
47
|
+
end
|
48
|
+
return kids[0].get(grandchild) if (!kids.empty?)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Not found
|
52
|
+
return nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_completions(path)
|
56
|
+
# Return all children if path is nil or empty
|
57
|
+
return children if (path.nil? || path.empty?)
|
58
|
+
|
59
|
+
# Split path on / or :
|
60
|
+
child, recurse, grandchild = path.partition("/")
|
61
|
+
if (recurse.empty?)
|
62
|
+
child, recurse, grandchild = path.partition(":")
|
63
|
+
end
|
64
|
+
|
65
|
+
case child
|
66
|
+
when "."
|
67
|
+
# Same directory
|
68
|
+
return get_completions(grandchild)
|
69
|
+
when ".."
|
70
|
+
# Check parent if it exists
|
71
|
+
return @parent.get_completions(grandchild) if (@parent)
|
72
|
+
|
73
|
+
# Otherwise we reached top-level so just keep going
|
74
|
+
return get_completions(grandchild)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Check child if it exists
|
78
|
+
if (!recurse.empty? && has_child?(child))
|
79
|
+
return get(child).get_completions(grandchild)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Return any children that may match
|
83
|
+
return children.select do |kid|
|
84
|
+
kid.name.downcase.start_with?(child.downcase)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def has_child?(name)
|
89
|
+
return children.any? do |child|
|
90
|
+
child.name == name
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def initialize(name, parent)
|
95
|
+
@kids = nil
|
96
|
+
@name = name
|
97
|
+
@parent = parent
|
98
|
+
end
|
99
|
+
|
100
|
+
def tab_complete(color = false)
|
101
|
+
color ||= false
|
102
|
+
return {@name => desc}
|
103
|
+
end
|
104
|
+
|
105
|
+
def to_s
|
106
|
+
return @name
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
require "git_xplorer/git_object/directory"
|
111
|
+
require "git_xplorer/git_object/file"
|
112
|
+
require "git_xplorer/git_object/revision"
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "djinni"
|
2
|
+
|
3
|
+
class CatWish < Djinni::Wish
|
4
|
+
def aliases
|
5
|
+
return ["cat"]
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
return "Display file contents"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(args, djinni_env = Hash.new)
|
13
|
+
gitx = djinni_env["gitXplorer"]
|
14
|
+
|
15
|
+
begin
|
16
|
+
gitx.show(args)
|
17
|
+
rescue GitXplorer::Error => e
|
18
|
+
puts e.message
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def tab_complete(input, djinni_env = Hash.new)
|
23
|
+
gitx = djinni_env["gitXplorer"]
|
24
|
+
|
25
|
+
_, found, partial = input.rpartition(":")
|
26
|
+
_, _, partial = input.rpartition("/") if (found.empty?)
|
27
|
+
|
28
|
+
completions = Hash.new
|
29
|
+
gitx.get_completions(input).each do |child|
|
30
|
+
completions.merge!(child.tab_complete)
|
31
|
+
end
|
32
|
+
|
33
|
+
return [completions, partial, ""]
|
34
|
+
end
|
35
|
+
|
36
|
+
def usage
|
37
|
+
puts "#{aliases.join(", ")} <file>"
|
38
|
+
puts " #{description}."
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "djinni"
|
2
|
+
require "hilighter"
|
3
|
+
|
4
|
+
class ChangeDirectoryWish < Djinni::Wish
|
5
|
+
def aliases
|
6
|
+
return ["cd"]
|
7
|
+
end
|
8
|
+
|
9
|
+
def description
|
10
|
+
return "Change to new directory"
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(args, djinni_env = Hash.new)
|
14
|
+
gitx = djinni_env["gitXplorer"]
|
15
|
+
begin
|
16
|
+
gitx.cd(args)
|
17
|
+
prompt = "#{gitx.pwd_short}$ ".light_white
|
18
|
+
djinni_env["djinni_prompt"] = prompt
|
19
|
+
rescue GitXplorer::Error => e
|
20
|
+
puts e.message
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def tab_complete(input, djinni_env = Hash.new)
|
25
|
+
gitx = djinni_env["gitXplorer"]
|
26
|
+
|
27
|
+
_, _, partial = input.rpartition("/")
|
28
|
+
partial = "" if (input.end_with?("/"))
|
29
|
+
|
30
|
+
completions = Hash.new
|
31
|
+
gitx.get_completions(input).select do |child|
|
32
|
+
child.is_a?(GitXplorer::GitObject::Directory)
|
33
|
+
end.each do |child|
|
34
|
+
completions.merge!(child.tab_complete)
|
35
|
+
end
|
36
|
+
|
37
|
+
return [completions, partial, ""]
|
38
|
+
end
|
39
|
+
|
40
|
+
def usage
|
41
|
+
puts "#{aliases.join(", ")} [directory]"
|
42
|
+
puts " #{description}."
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "djinni"
|
2
|
+
|
3
|
+
class FindWish < Djinni::Wish
|
4
|
+
def aliases
|
5
|
+
return ["find"]
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
return "Similar to \"find . -iregex PATTERN\""
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(args, djinni_env = Hash.new)
|
13
|
+
gitx = djinni_env["gitXplorer"]
|
14
|
+
args.gsub!(/^(["'])(.*)\1$/, "\\2")
|
15
|
+
dir = gitx.pwd.gsub(%r{^[^/]+(/|$)}, "")
|
16
|
+
|
17
|
+
begin
|
18
|
+
puts gitx.get_filenames(dir, args)
|
19
|
+
rescue GitXplorer::Error => e
|
20
|
+
puts e.message
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def usage
|
25
|
+
puts "#{aliases.join(", ")} [PATTERN]"
|
26
|
+
puts " #{description}."
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "djinni"
|
2
|
+
|
3
|
+
class GitWish < Djinni::Wish
|
4
|
+
def aliases
|
5
|
+
return ["git"]
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
return "Run any git command"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(args, djinni_env = Hash.new)
|
13
|
+
gitx = djinni_env["gitXplorer"]
|
14
|
+
begin
|
15
|
+
case args
|
16
|
+
when /^\s*(diff|ls-files|rev-parse|show|status).*/
|
17
|
+
puts gitx.git(args, false)
|
18
|
+
else
|
19
|
+
puts gitx.git(args)
|
20
|
+
end
|
21
|
+
rescue GitXplorer::Error => e
|
22
|
+
puts e.message
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def usage
|
27
|
+
puts "#{aliases.join(", ")}"
|
28
|
+
puts " #{description}."
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "djinni"
|
2
|
+
|
3
|
+
class GrepWish < Djinni::Wish
|
4
|
+
def aliases
|
5
|
+
return ["grep"]
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
return "Similar to \"git grep -HIinP PATTERN\""
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(args, djinni_env = Hash.new)
|
13
|
+
gitx = djinni_env["gitXplorer"]
|
14
|
+
args.gsub!(/^(["'])(.*)\1$/, "\\2")
|
15
|
+
dir = gitx.pwd.gsub(%r{^[^/]+(/|$)}, "")
|
16
|
+
|
17
|
+
if (args.empty?)
|
18
|
+
usage
|
19
|
+
return
|
20
|
+
end
|
21
|
+
|
22
|
+
begin
|
23
|
+
gitx.search(dir, args).each do |line|
|
24
|
+
m = line.match(/^([^:]+):([^:]+):(\d+):(.*)$/)
|
25
|
+
if (m && (m.length == 5))
|
26
|
+
puts [
|
27
|
+
m[1].green,
|
28
|
+
m[2].cyan,
|
29
|
+
m[3].white,
|
30
|
+
m[4].gsub(/(#{args})/i, "\\1".black.on_white)
|
31
|
+
].join(":")
|
32
|
+
else
|
33
|
+
puts line
|
34
|
+
end
|
35
|
+
end
|
36
|
+
rescue GitXplorer::Error => e
|
37
|
+
puts e.message
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def usage
|
42
|
+
puts "#{aliases.join(", ")} <PATTERN>"
|
43
|
+
puts " #{description}."
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require "djinni"
|
2
|
+
|
3
|
+
class ListDirectoryWish < Djinni::Wish
|
4
|
+
def aliases
|
5
|
+
return ["ls", "ll"]
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
return "List directory contents or file revisions"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(args, djinni_env = Hash.new)
|
13
|
+
gitx = djinni_env["gitXplorer"]
|
14
|
+
|
15
|
+
long = false
|
16
|
+
case djinni_env["djinni_input"]
|
17
|
+
when "ll"
|
18
|
+
long = true
|
19
|
+
end
|
20
|
+
if (args.match(/(^| )(-l|--long)( |$)/))
|
21
|
+
args.gsub!(/(^| )(-l|--long)( |$)/, "")
|
22
|
+
long = true
|
23
|
+
end
|
24
|
+
|
25
|
+
begin
|
26
|
+
children = gitx.ls(args)
|
27
|
+
if (long)
|
28
|
+
long(children)
|
29
|
+
else
|
30
|
+
children.each do |child|
|
31
|
+
puts child
|
32
|
+
end
|
33
|
+
end
|
34
|
+
rescue GitXplorer::Error => e
|
35
|
+
puts e.message
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize
|
40
|
+
@space = 4
|
41
|
+
end
|
42
|
+
|
43
|
+
def long(children)
|
44
|
+
max = children.max_by do |child|
|
45
|
+
child.to_s.length
|
46
|
+
end.to_s.length
|
47
|
+
nlfill = " " * (max + @space)
|
48
|
+
width = %x(tput cols).to_i - (max + @space) - 2
|
49
|
+
|
50
|
+
children.each do |child|
|
51
|
+
name = child.to_s
|
52
|
+
desc = child.desc
|
53
|
+
fill = " " * (max + @space - name.length)
|
54
|
+
fill = "" if ((max + @space - name.length) <= 0)
|
55
|
+
lines = Array.new
|
56
|
+
if (desc)
|
57
|
+
lines = desc.scan(/\S.{0,#{width}}\S(?=\s|$)|\S+/)
|
58
|
+
end
|
59
|
+
if (lines.empty?)
|
60
|
+
puts name
|
61
|
+
else
|
62
|
+
start = lines.delete_at(0)
|
63
|
+
puts "#{name}#{fill}#{start}"
|
64
|
+
lines.each do |line|
|
65
|
+
puts "#{nlfill}#{line}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
private :long
|
71
|
+
|
72
|
+
def tab_complete(input, djinni_env = Hash.new)
|
73
|
+
gitx = djinni_env["gitXplorer"]
|
74
|
+
|
75
|
+
_, found, partial = input.rpartition(":")
|
76
|
+
_, _, partial = input.rpartition("/") if (found.empty?)
|
77
|
+
|
78
|
+
completions = Hash.new
|
79
|
+
gitx.get_completions(input).each do |child|
|
80
|
+
completions.merge!(child.tab_complete)
|
81
|
+
end
|
82
|
+
|
83
|
+
return [completions, partial, ""]
|
84
|
+
end
|
85
|
+
|
86
|
+
def usage
|
87
|
+
puts "#{aliases.join(", ")} [OPTIONS] [directory/file]"
|
88
|
+
puts " #{description}."
|
89
|
+
puts
|
90
|
+
puts "OPTIONS"
|
91
|
+
puts " -l, --long Show more info for files"
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "djinni"
|
2
|
+
|
3
|
+
class PwdWish < Djinni::Wish
|
4
|
+
def aliases
|
5
|
+
return ["pwd"]
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
return "Show path of current path"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(args, djinni_env = Hash.new)
|
13
|
+
if (!args.empty?)
|
14
|
+
usage
|
15
|
+
else
|
16
|
+
puts djinni_env["gitXplorer"].pwd
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def usage
|
21
|
+
puts aliases.join(", ")
|
22
|
+
puts " #{description}."
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "djinni"
|
2
|
+
|
3
|
+
class ViWish < Djinni::Wish
|
4
|
+
def aliases
|
5
|
+
return ["vi", "view", "vim"]
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
return "Open the file in vim"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(args, djinni_env = Hash.new)
|
13
|
+
gitx = djinni_env["gitXplorer"]
|
14
|
+
|
15
|
+
readonly = false
|
16
|
+
case djinni_env["djinni_input"]
|
17
|
+
when "view"
|
18
|
+
readonly = true
|
19
|
+
end
|
20
|
+
|
21
|
+
begin
|
22
|
+
gitx.vim(args, readonly)
|
23
|
+
rescue GitXplorer::Error => e
|
24
|
+
puts e.message
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def tab_complete(input, djinni_env = Hash.new)
|
29
|
+
gitx = djinni_env["gitXplorer"]
|
30
|
+
|
31
|
+
_, found, partial = input.rpartition(":")
|
32
|
+
_, _, partial = input.rpartition("/") if (found.empty?)
|
33
|
+
|
34
|
+
completions = Hash.new
|
35
|
+
gitx.get_completions(input).each do |child|
|
36
|
+
completions.merge!(child.tab_complete)
|
37
|
+
end
|
38
|
+
|
39
|
+
return [completions, partial, ""]
|
40
|
+
end
|
41
|
+
|
42
|
+
def usage
|
43
|
+
puts "#{aliases.join(", ")} <file>"
|
44
|
+
puts " #{description}."
|
45
|
+
end
|
46
|
+
end
|