ezgit 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +135 -0
- data/bin/ez +3 -0
- data/bin/ez.bat +2 -0
- data/bin/wac.exe +0 -0
- data/lib/ezgit.rb +5 -0
- data/lib/ezgit/commands.rb +71 -0
- data/lib/ezgit/commands/clean!.rb +10 -0
- data/lib/ezgit/commands/clean.rb +10 -0
- data/lib/ezgit/commands/clone.rb +9 -0
- data/lib/ezgit/commands/commit.rb +9 -0
- data/lib/ezgit/commands/create.rb +9 -0
- data/lib/ezgit/commands/delete.rb +11 -0
- data/lib/ezgit/commands/goto.rb +11 -0
- data/lib/ezgit/commands/info.rb +10 -0
- data/lib/ezgit/commands/move.rb +10 -0
- data/lib/ezgit/commands/pull.rb +8 -0
- data/lib/ezgit/commands/push.rb +8 -0
- data/lib/ezgit/commands/reset.rb +10 -0
- data/lib/ezgit/commands/switch!.rb +12 -0
- data/lib/ezgit/commands/switch.rb +10 -0
- data/lib/ezgit/commands/tree.rb +13 -0
- data/lib/ezgit/commands/update.rb +9 -0
- data/lib/ezgit/ez_command.rb +22 -0
- data/lib/ezgit/git.rb +399 -0
- data/lib/ezgit/run.rb +9 -0
- data/lib/ezgit/string.rb +50 -0
- data/lib/ezgit/trollop.rb +790 -0
- data/lib/ezgit/version.rb +3 -0
- metadata +74 -0
data/README.md
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
## EZGit
|
2
|
+
=====
|
3
|
+
|
4
|
+
(This project is currently under development)
|
5
|
+
|
6
|
+
* http://github.com/ajjames/ezgit
|
7
|
+
* http://rubygems.org/gems/ezgit
|
8
|
+
|
9
|
+
|
10
|
+
## DESCRIPTION
|
11
|
+
|
12
|
+
EZGit is a command-line interface based on Ruby. The **goal** of **EZGit** is to simplify the daily git tasks used in a multi-team enterprise development environment without the need to fully understand the full complex beauty of Git. With EZGit, committers with any level of Git knowledge can work in the repo with confidence and consistency.
|
13
|
+
|
14
|
+
EZGit abstracts away many Git concepts that are consistent sources of confusion. Concepts such as...
|
15
|
+
* _**Where do branches live? On my local machine or on the remote?**_ Who cares? EZGit handles it for you.
|
16
|
+
* _**What's the difference between the working directory, the stage(or index), and the local repo?**_ I could spend hours explaining it to you, but if you don't really want to know, EZGit is here to make that stuff go away.
|
17
|
+
|
18
|
+
EZGit replaces git's complex and overloaded commands with simple, intentional, ruby-styled names.
|
19
|
+
|
20
|
+
In future updates, EZGit will implement a branching strategy that has been honed and refined over the last two years by a large agile enterprise development shop. The concepts of merging (and rebasing) will be equally simplified so that you can concentrate on your code, and not on, "how the heck do I merge these changes?!"
|
21
|
+
|
22
|
+
Best of all, EZGit is still Git. So if you are a Git-Fu master, you can go back to using your favorite, obtuse Git commands at any time. They'll always be there waiting for you when you need to do the complex stuff.
|
23
|
+
|
24
|
+
If you try EZGit and find that it doesn't quite work for you, drop me a line and let me know why. I'm happy to look at other use cases and consider including them in future updates.
|
25
|
+
|
26
|
+
Cheers,
|
27
|
+
|
28
|
+
AJ
|
29
|
+
|
30
|
+
|
31
|
+
## INSTALLATION
|
32
|
+
|
33
|
+
To intall:
|
34
|
+
|
35
|
+
gem install ezgit
|
36
|
+
|
37
|
+
EZGit changes a lot (especially during this development phase). To update your existing installs, you can use `gem update ezgit`, or you can use EZGit's self-update command:
|
38
|
+
|
39
|
+
ez update
|
40
|
+
|
41
|
+
|
42
|
+
## HELP TEXT
|
43
|
+
|
44
|
+
Once installed, take a look at the help menu:
|
45
|
+
|
46
|
+
ez -h
|
47
|
+
|
48
|
+
|
49
|
+
EZGit is a simple interface for working with git repositories.
|
50
|
+
|
51
|
+
Usage:
|
52
|
+
ez [<options>] <commands>
|
53
|
+
|
54
|
+
commands are:
|
55
|
+
clean! Wipes all untracked and ignored files.
|
56
|
+
clean Wipes all untracked files, but allows ignored files to remain.
|
57
|
+
clone Creates a copy of a repository.
|
58
|
+
commit Creates a commit for all current file changes.
|
59
|
+
create Create a new branch in the current state.
|
60
|
+
delete! Completely delete a given branch.
|
61
|
+
goto! Move to the location in the tree specified by a commit id. All changes will be lost.
|
62
|
+
info Shows files status and current branches.
|
63
|
+
move Switch and move changes to the given branch.
|
64
|
+
pull Pulls the latest changes from the remote.
|
65
|
+
push Pushes the current branch changes to the remote.
|
66
|
+
reset! Deletes changes in all tracked files. Untracked files will not be affected.
|
67
|
+
switch! Abandon all changes and switch to the given branch.
|
68
|
+
switch Switch to the given branch if there are not changes.
|
69
|
+
tree Shows git tree history for the current branch
|
70
|
+
update Attempts to self-update from rubygems.org.
|
71
|
+
|
72
|
+
options are:
|
73
|
+
--dry-run-flag, -n: Forces all commands to be passive.
|
74
|
+
--debug, -d: Shows command level debug info.
|
75
|
+
--version, -v: Print version and exit
|
76
|
+
--help, -h: Show this message
|
77
|
+
|
78
|
+
|
79
|
+
## EXAMPLE
|
80
|
+
|
81
|
+
```
|
82
|
+
~/git/test_repo > git init
|
83
|
+
Initialized empty Git repository in /Users/AJ/git/test_repo/.git/
|
84
|
+
|
85
|
+
~/git/test_repo > touch file1 file2 file3 .gitignore
|
86
|
+
|
87
|
+
~/git/test_repo > ez info
|
88
|
+
________________________________
|
89
|
+
|
90
|
+
REPOSITORY TREE(last 5 commits)
|
91
|
+
There is no history yet.
|
92
|
+
|
93
|
+
BRANCHES:
|
94
|
+
|
95
|
+
TO BE COMMITTED ON: master
|
96
|
+
Add .gitignore
|
97
|
+
Add file1
|
98
|
+
Add file2
|
99
|
+
Add file3
|
100
|
+
|
101
|
+
SYNC STATUS:
|
102
|
+
Your master branch does not yet exist on the remote.
|
103
|
+
(Use 'ez pull' to update the remote.)
|
104
|
+
________________________________
|
105
|
+
|
106
|
+
~/git/test_repo > ez commit "initial commit"
|
107
|
+
|
108
|
+
[master (root-commit) e315617] initial commit
|
109
|
+
0 files changed
|
110
|
+
create mode 100644 .gitignore
|
111
|
+
create mode 100644 file1
|
112
|
+
create mode 100644 file2
|
113
|
+
create mode 100644 file3
|
114
|
+
|
115
|
+
REPOSITORY TREE(last 5 commits)
|
116
|
+
* e315617 (0 seconds ago) AJ James initial commit (HEAD, master)
|
117
|
+
|
118
|
+
TO BE COMMITTED ON: master
|
119
|
+
No changes.
|
120
|
+
|
121
|
+
SYNC STATUS:
|
122
|
+
Your master branch does not yet exist on the remote.
|
123
|
+
(Use 'ez pull' to update the remote.)
|
124
|
+
```
|
125
|
+
|
126
|
+
|
127
|
+
## ISSUES
|
128
|
+
|
129
|
+
To report issues at:
|
130
|
+
https://github.com/ajjames/ezgit/issues
|
131
|
+
|
132
|
+
|
133
|
+
## ACKNOWLEDGEMENTS
|
134
|
+
|
135
|
+
This project makes use of Trollop (Copyright 2007 William Morgan). An excellent light-weight command-line parser.
|
data/bin/ez
ADDED
data/bin/ez.bat
ADDED
data/bin/wac.exe
ADDED
Binary file
|
data/lib/ezgit.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'ezgit/git'
|
2
|
+
|
3
|
+
class Commands
|
4
|
+
|
5
|
+
attr_accessor :all, :symbols, :names, :help_list, :options, :git
|
6
|
+
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@all = []
|
10
|
+
@symbols = []
|
11
|
+
@names = []
|
12
|
+
@help_list = ''
|
13
|
+
@options = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def read
|
18
|
+
commands_dir = File.expand_path "commands", File.dirname(__FILE__)
|
19
|
+
files = Dir["#{commands_dir}/*.rb"]
|
20
|
+
files.each do |command_file|
|
21
|
+
require command_file
|
22
|
+
current_symbol = @all.last[:name]
|
23
|
+
@symbols << current_symbol
|
24
|
+
@names << current_symbol.to_s
|
25
|
+
@help_list << "\t#{@names.last.cyan.bold}\t#{@all.last[:help].to_s.bold}\n"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def process
|
31
|
+
@git = Git.new(@options)
|
32
|
+
@cmd = ARGV.shift
|
33
|
+
matched = false
|
34
|
+
@all.each do |current|
|
35
|
+
if current[:name].to_s.eql? @cmd
|
36
|
+
matched = true
|
37
|
+
@cmd_opts = Trollop::options do
|
38
|
+
usage = current[:usage]
|
39
|
+
usage ||= "ez #{current[:name].to_s} [<options>]"
|
40
|
+
banner <<-HELP_DESCRIPTION
|
41
|
+
command: ez #{current[:name].to_s}\n
|
42
|
+
#{current[:help].to_s}
|
43
|
+
|
44
|
+
Usage:
|
45
|
+
#{usage}
|
46
|
+
|
47
|
+
options are:
|
48
|
+
HELP_DESCRIPTION
|
49
|
+
|
50
|
+
current[:options].each do |cmd_opt|
|
51
|
+
sym = cmd_opt[0]
|
52
|
+
info = cmd_opt[1]
|
53
|
+
flags = cmd_opt[2]
|
54
|
+
opt sym, info, flags
|
55
|
+
end
|
56
|
+
end
|
57
|
+
current[:action].call(@cmd_opts, ARGV)
|
58
|
+
break
|
59
|
+
end
|
60
|
+
end
|
61
|
+
Trollop::die "unknown subcommand #{@cmd.inspect}" if not matched
|
62
|
+
if $commands.options[:debug]
|
63
|
+
puts "Global options: #{$commands.options.inspect}"
|
64
|
+
puts "Subcommand: #{@cmd.inspect}"
|
65
|
+
puts "Subcommand options: #{@cmd_opts.inspect}"
|
66
|
+
puts "Remaining arguments: #{ARGV.inspect}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
$commands.all << {
|
2
|
+
name: 'clean!',
|
3
|
+
help: 'Wipes all untracked and ignored files.',
|
4
|
+
options: [
|
5
|
+
[:force, 'Automatically approve and bypass the confirmation prompt.', short: '-f']
|
6
|
+
],
|
7
|
+
action: lambda do |opts, args|
|
8
|
+
$commands.git.clean!(true, opts)
|
9
|
+
end
|
10
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
$commands.all << {
|
2
|
+
name: 'clean',
|
3
|
+
help: 'Wipes all untracked files, but allows ignored files to remain.',
|
4
|
+
options: [
|
5
|
+
[:force, 'Automatically approve and bypass the confirmation prompt.', short: '-f']
|
6
|
+
],
|
7
|
+
action: lambda do |opts, args|
|
8
|
+
$commands.git.clean!(false, opts)
|
9
|
+
end
|
10
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
$commands.all << {
|
2
|
+
name: 'delete!',
|
3
|
+
help: 'Completely delete a given branch.',
|
4
|
+
usage: 'ez delete! <branch_name>',
|
5
|
+
options: [
|
6
|
+
[:force, 'Automatically approve and bypass the confirmation prompt.', short: '-f']
|
7
|
+
],
|
8
|
+
action: lambda do |opts, args|
|
9
|
+
$commands.git.delete!(opts,args)
|
10
|
+
end
|
11
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
$commands.all << {
|
2
|
+
name: 'goto!',
|
3
|
+
help: 'Move to the location in the tree specified by a commit id. All changes will be lost.',
|
4
|
+
usage: 'ez goto! <commit>',
|
5
|
+
options: [
|
6
|
+
[:force, 'Automatically approve and bypass the confirmation prompt.', short: '-f']
|
7
|
+
],
|
8
|
+
action: lambda do |opts, args|
|
9
|
+
$commands.git.goto!(opts, args)
|
10
|
+
end
|
11
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
$commands.all << {
|
2
|
+
name: 'reset!',
|
3
|
+
help: 'Deletes changes in all tracked files. Untracked files will not be affected.',
|
4
|
+
options: [
|
5
|
+
[:force, 'Automatically approve and bypass the confirmation prompt.', short: '-f']
|
6
|
+
],
|
7
|
+
action: lambda do |opts, args|
|
8
|
+
$commands.git.reset_hard!(opts)
|
9
|
+
end
|
10
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
$commands.all << {
|
2
|
+
name: 'switch!',
|
3
|
+
help: 'Abandon all changes and switch to the given branch.',
|
4
|
+
usage: 'ez switch! <branch_name>',
|
5
|
+
options: [
|
6
|
+
[:force, 'Automatically approve and bypass the confirmation prompt.', short: '-f']
|
7
|
+
],
|
8
|
+
action: lambda do |opts, args|
|
9
|
+
opts[:switch!] = true
|
10
|
+
$commands.git.switch!(opts, args)
|
11
|
+
end
|
12
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
$commands.all << {
|
2
|
+
name: 'tree',
|
3
|
+
help: 'Shows git tree history for the current branch',
|
4
|
+
usage: 'ez tree',
|
5
|
+
options: [
|
6
|
+
[:current, 'Shows only the current branch. All other branches are filtered out.', short: '-c'],
|
7
|
+
[:number, 'Number of entries to display.', short: '-n', default:20]
|
8
|
+
],
|
9
|
+
action: lambda do |opts, args|
|
10
|
+
count = args[0] || 20
|
11
|
+
$commands.git.display_log_graph(opts[:number], !opts[:current])
|
12
|
+
end
|
13
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'ezgit/version'
|
2
|
+
|
3
|
+
class EzCommand
|
4
|
+
def self.options(subcommand_names, subcommand_help_list)
|
5
|
+
return Trollop::options do
|
6
|
+
version Ezgit::VERSION
|
7
|
+
banner <<-HELP_DESCRIPTION
|
8
|
+
EZGit is a simple interface for working with git repositories.
|
9
|
+
|
10
|
+
Usage:
|
11
|
+
ez [<options>] <commands>
|
12
|
+
|
13
|
+
commands are:
|
14
|
+
#{subcommand_help_list}
|
15
|
+
options are:
|
16
|
+
HELP_DESCRIPTION
|
17
|
+
opt :dry_run_flag, 'Forces all commands to be passive.', short: '-n'
|
18
|
+
opt :debug, 'Shows command level debug info.', short: '-d'
|
19
|
+
stop_on subcommand_names
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/ezgit/git.rb
ADDED
@@ -0,0 +1,399 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
class Git
|
4
|
+
|
5
|
+
attr_reader :current_branch, :remote_branch, :all_branches, :all_uniq_branches
|
6
|
+
NO_BRANCH = '(nobranch)'
|
7
|
+
|
8
|
+
|
9
|
+
def initialize(global_options)
|
10
|
+
@its_a_dry_run = global_options[:dry_run_flag]
|
11
|
+
@dry_run_flag = @its_a_dry_run ? '-n' : ''
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
def current_branch
|
16
|
+
if @current_branch.nil?
|
17
|
+
remove_refs_regex = /.+\/(\w+)/
|
18
|
+
out = `git symbolic-ref HEAD 2>&1`.match(remove_refs_regex)
|
19
|
+
@current_branch = (out.nil?) ? NO_BRANCH : out[1].to_s
|
20
|
+
end
|
21
|
+
return @current_branch
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def remote_branch(branch_name = nil)
|
26
|
+
if @remote_branch.nil?
|
27
|
+
@remote_branch = add_remote_to_branch(current_branch)
|
28
|
+
end
|
29
|
+
return @remote_branch
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def add_remote_to_branch(branch_name)
|
34
|
+
# Get remote name. It may not be 'origin'
|
35
|
+
origin = `git remote show`.gsub(/\s/, '')
|
36
|
+
remote = "#{origin}/#{branch_name}"
|
37
|
+
remote = all_branches.include?(remote) ? remote : ''
|
38
|
+
return remote
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def refresh_branches
|
43
|
+
@all_branches = nil
|
44
|
+
@all_uniq_branches = nil
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
def all_branches
|
49
|
+
if @all_branches.nil?
|
50
|
+
# create regexes to remove the refs, HEAD entry, spaces, *, etc
|
51
|
+
strip_head_regx = /^.*\/HEAD -> .*$\n/
|
52
|
+
strip_asterisks_and_spaces_regx = /[\* ]/
|
53
|
+
strip_remotes_regex = /remotes\//
|
54
|
+
git_a = `git branch -a --no-color`.gsub(strip_head_regx, '').gsub(strip_asterisks_and_spaces_regx, '').gsub(strip_remotes_regex, '')
|
55
|
+
@all_branches = git_a.split("\n")
|
56
|
+
end
|
57
|
+
return @all_branches
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
def all_uniq_branches
|
62
|
+
if @all_uniq_branches.nil?
|
63
|
+
remove_refs_regx = /([\* ])|(.*\/)/
|
64
|
+
@all_uniq_branches = []
|
65
|
+
all_branches.each do |br|
|
66
|
+
@all_uniq_branches << br.gsub(remove_refs_regx, '')
|
67
|
+
end
|
68
|
+
@all_uniq_branches.uniq!
|
69
|
+
@all_uniq_branches.sort!
|
70
|
+
end
|
71
|
+
return @all_uniq_branches
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def display_log_graph(count = 5, show_all = false)
|
76
|
+
puts ''
|
77
|
+
puts "REPOSITORY TREE".white.bold + "(last #{count} commits)"
|
78
|
+
all = show_all ? '--all' : ''
|
79
|
+
stdin, stdout, stderr = Open3.popen3("git log --graph #{all} --format=format:\"#{CYAN}%h #{CLEAR + CYAN}(%cr) #{CYAN}%cn #{CLEAR + WHITE}%s#{CYAN + BOLD}%d#{CLEAR}\" --abbrev-commit --date=relative -n #{count}")
|
80
|
+
err = stderr.readlines
|
81
|
+
return puts 'There is no history yet.'.cyan.bold if err.any?
|
82
|
+
puts err.join('') + stdout.readlines.join('')
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def display_branch_list_with_current
|
87
|
+
puts ''
|
88
|
+
puts ' BRANCHES:'.bold
|
89
|
+
brs = []
|
90
|
+
all_uniq_branches.each do |b|
|
91
|
+
#add an indicator if it is the current branch
|
92
|
+
b = b.eql?(current_branch) ? "#{b} <-- CURRENT".bold : b
|
93
|
+
# output the list
|
94
|
+
puts " #{b}".cyan
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
def info(opts=nil)
|
100
|
+
puts '________________________________'
|
101
|
+
$commands.git.display_log_graph
|
102
|
+
$commands.git.display_branch_list_with_current
|
103
|
+
$commands.git.display_current_changes(opts)
|
104
|
+
$commands.git.display_sync_status
|
105
|
+
puts '________________________________'
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
#returns :up_to_date/:no_remote/:rebase/:ahead/:behind, count
|
110
|
+
def check_remote_status
|
111
|
+
return :headless if current_branch == NO_BRANCH
|
112
|
+
ahead_count_rgx = /.*ahead.(\d+)/
|
113
|
+
behind_count_rgx = /.*behind.(\d+)/
|
114
|
+
stdin, stdout, stderr = Open3.popen3('git status -bs')
|
115
|
+
stat = stdout.readlines[0]
|
116
|
+
ahead_match = stat.match(ahead_count_rgx)
|
117
|
+
ahead_count = (ahead_match.nil?) ? '0' : ahead_match[1]
|
118
|
+
behind_match = stat.match(behind_count_rgx)
|
119
|
+
behind_count = (behind_match.nil?) ? '0' : behind_match[1]
|
120
|
+
case
|
121
|
+
when ahead_count > '0' && behind_count == '0'
|
122
|
+
return :ahead, ahead_count
|
123
|
+
when ahead_count == '0' && behind_count > '0'
|
124
|
+
return :behind, behind_count
|
125
|
+
when ahead_count > '0' && behind_count > '0'
|
126
|
+
return :rebase, '0'
|
127
|
+
else
|
128
|
+
return :no_remote, '0' if remote_branch.empty?
|
129
|
+
return :up_to_date, '0'
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
def display_sync_status
|
135
|
+
puts ''
|
136
|
+
puts ' SYNC STATUS:'.white.bold
|
137
|
+
stat, count = check_remote_status
|
138
|
+
commit_s = (count == 1) ? 'commit' : 'commits'
|
139
|
+
case stat
|
140
|
+
when :ahead
|
141
|
+
puts " Your #{current_branch.bold + CYAN} branch is ahead of the remote by #{count} #{commit_s}.".cyan
|
142
|
+
puts " (Use 'ez pull' to update the remote.)".cyan
|
143
|
+
when :behind
|
144
|
+
puts " Your #{current_branch.bold + YELLOW} branch is behind the remote by #{count} #{commit_s}.".yellow
|
145
|
+
puts " (Use 'ez pull' to get the new changes.)".yellow
|
146
|
+
when :rebase
|
147
|
+
puts " Your #{current_branch} branch has diverged #{count} #{commit_s} from the remote.".red.bold
|
148
|
+
puts " (Use must use git directly to put them back in sync.)".red.bold
|
149
|
+
when :no_remote
|
150
|
+
puts " Your #{current_branch.bold + CYAN} branch does not yet exist on the remote.".cyan
|
151
|
+
puts " (Use 'ez pull' to update the remote.)".cyan
|
152
|
+
when :headless
|
153
|
+
puts " You are in a headless state (not on a branch)".red.bold
|
154
|
+
puts " (Use 'ez create <branch>' to create a branch at this commit,".red.bold
|
155
|
+
puts " or use 'ez switch <branch>' to switch to a branch.)".red.bold
|
156
|
+
else
|
157
|
+
puts " Your #{current_branch.bold + GREEN} branch is in sync with the remote.".green
|
158
|
+
puts " (Use 'ez pull' to ensure it stays in sync.)".green
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
#returns (bool has_changes?, Array changes)
|
164
|
+
def check_local_changes(opts = nil)
|
165
|
+
ignored = (opts.nil? || opts[:ignored] == false) ? '' : '--ignored'
|
166
|
+
stdin, stdout, stderr = Open3.popen3("git status --untracked-files=all --porcelain #{ignored}")
|
167
|
+
changes = stdout.readlines
|
168
|
+
return changes.any?, changes
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
def display_current_changes(opts = nil)
|
173
|
+
puts ''
|
174
|
+
puts " TO BE COMMITTED ON: #{current_branch}".white.bold
|
175
|
+
has_changes, changes = check_local_changes(opts)
|
176
|
+
puts " No changes.".green unless has_changes
|
177
|
+
changes.collect! { |line|
|
178
|
+
line.sub!('!! ', CYAN + " ignore " + CLEAR)
|
179
|
+
line.gsub!(/ U |U /, RED + BOLD + " MERGE " + CLEAR)
|
180
|
+
line.gsub!(/ D |D /, RED + BOLD + " Delete " + CLEAR)
|
181
|
+
line.gsub!(/.R |R. /, YELLOW + BOLD + " Rename " + CLEAR)
|
182
|
+
line.gsub!(/A |\?\? /, YELLOW + BOLD + " Add " + CLEAR)
|
183
|
+
line.gsub!(/.M |M. /, YELLOW + BOLD + " Change " + CLEAR)
|
184
|
+
line
|
185
|
+
}
|
186
|
+
puts changes.sort!
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
def clone(args)
|
191
|
+
return puts 'invalid number of arguments. Requires a source. (Destination is optional.)' if args.count < 1 || args.count > 2
|
192
|
+
return if @its_a_dry_run
|
193
|
+
puts out = `git clone #{args.first} #{args[1]}`
|
194
|
+
repo_name = args[1] || out.split('\'')[1]
|
195
|
+
puts 'You have created a copy of ' + args.first.to_s.bold + ' in the ' + repo_name.bold + ' directory.' if $? == 0
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
def clean!(wipe_ignored, opts)
|
200
|
+
x = wipe_ignored ? 'x' : ''
|
201
|
+
stdin, stdout, stderr = Open3.popen3("git clean -dfn#{x}")
|
202
|
+
out = stderr.readlines
|
203
|
+
err = stdout.readlines.join('')
|
204
|
+
puts err.red.bold unless opts[:force]
|
205
|
+
return puts 'Nothing to clean.'.green if err.empty?
|
206
|
+
return if @its_a_dry_run
|
207
|
+
run_lambda_with_force_option(opts) do
|
208
|
+
puts `git clean -df#{x} #{@dry_run}`
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
def prompt_for_y_n
|
214
|
+
begin
|
215
|
+
system("stty raw -echo")
|
216
|
+
input = STDIN.getc
|
217
|
+
ensure
|
218
|
+
system("stty -raw echo")
|
219
|
+
end
|
220
|
+
out = input.to_s.downcase.eql?('y')
|
221
|
+
puts input.to_s
|
222
|
+
return out
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
def run_lambda_with_force_option(opts)
|
227
|
+
unless opts[:force]
|
228
|
+
print 'proceed(y/n)? '.bold
|
229
|
+
return unless prompt_for_y_n
|
230
|
+
end
|
231
|
+
yield
|
232
|
+
end
|
233
|
+
|
234
|
+
|
235
|
+
def reset_hard!(opts)
|
236
|
+
return if @its_a_dry_run
|
237
|
+
puts 'All changes in tracked files will be lost.'.red.bold unless opts[:force]
|
238
|
+
run_lambda_with_force_option(opts) do
|
239
|
+
puts `git reset --hard`
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
|
244
|
+
def goto!(opts, args)
|
245
|
+
return puts "Please specify a commit id.".yellow.bold if args.count < 1
|
246
|
+
return puts "Invalid number of arguments. Please specify only a commit id.".yellow.bold if args.count > 1
|
247
|
+
commit_id = args[0].to_s
|
248
|
+
return puts "Would go to #{commit_id}" if @its_a_dry_run
|
249
|
+
puts "About to go to #{commit_id}. All changes in tracked files will be lost.".red.bold unless opts[:force]
|
250
|
+
run_lambda_with_force_option(opts) do
|
251
|
+
puts `git reset --hard #{commit_id}`
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
def create(opts, args)
|
257
|
+
return puts "Please specify a branch name.".yellow.bold if args.count < 1
|
258
|
+
return puts "Invalid number of arguments. Please specify only a branch name.".yellow.bold if args.count > 1
|
259
|
+
branch_name = args[0].to_s
|
260
|
+
return puts "Would create branch: #{branch_name}" if @its_a_dry_run
|
261
|
+
`git checkout -b #{branch_name}`
|
262
|
+
display_branch_list_with_current
|
263
|
+
display_current_changes
|
264
|
+
end
|
265
|
+
|
266
|
+
|
267
|
+
def delete!(opts, args)
|
268
|
+
return puts "Please specify a branch name.".yellow.bold if args.count < 1
|
269
|
+
return puts "Invalid number of arguments. Please specify only a branch name.".yellow.bold if args.count > 1
|
270
|
+
branch_name = args[0].to_s
|
271
|
+
is_master = branch_name.eql?('master') || branch_name.eql?(add_remote_to_branch('master'))
|
272
|
+
return puts "Cannot delete ".red + branch_name.red.bold + ".".red if is_master
|
273
|
+
branches = []
|
274
|
+
is_local = all_branches.include?(branch_name)
|
275
|
+
branches << branch_name if is_local
|
276
|
+
remote_name = add_remote_to_branch(branch_name)
|
277
|
+
is_remote = all_branches.include?(remote_name)
|
278
|
+
branches << remote_name if is_remote
|
279
|
+
return puts "Cannot delete ".red + branch_name.red.bold + " while you are using it. Please switch to another branch and try again.".red if branches.include?(current_branch)
|
280
|
+
return puts "Branch does not exist: ".red + branch_name.red.bold unless branches.any?
|
281
|
+
return puts "Would completely delete branches: #{branches.join(',')}" if @its_a_dry_run
|
282
|
+
print " Are you sure you want to delete '#{branch_name}'(y/n)?".red.bold
|
283
|
+
return unless run_lambda_with_force_option(opts) do
|
284
|
+
puts `git push --delete #{remote_name.sub('/', ' ')}` if is_remote
|
285
|
+
puts `git branch -D #{branch_name}` if is_local
|
286
|
+
refresh_branches
|
287
|
+
display_branch_list_with_current
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
|
292
|
+
#Three modes:
|
293
|
+
# :switch - switch if there are not changes. Otherwise halt!
|
294
|
+
# :switch! - clobber all files before switching
|
295
|
+
# :move - move files with switch
|
296
|
+
def switch!(opts, args)
|
297
|
+
return puts "Please specify a branch name.".yellow.bold if args.count < 1
|
298
|
+
return puts "Invalid number of arguments. Please specify only a branch name.".yellow.bold if args.count > 1
|
299
|
+
branch_name = args[0].to_s
|
300
|
+
return puts "Please specify a valid branch." unless all_uniq_branches.include?(branch_name)
|
301
|
+
return puts "Already on branch: #{current_branch.bold}".green if current_branch.eql?(branch_name)
|
302
|
+
has_changes, changes = check_local_changes
|
303
|
+
#move files with switch
|
304
|
+
if opts[:move]
|
305
|
+
x = `git checkout #{branch_name}`
|
306
|
+
return
|
307
|
+
end
|
308
|
+
#switch if there are not changes. Otherwise halt!
|
309
|
+
if has_changes && opts[:switch]
|
310
|
+
display_current_changes
|
311
|
+
puts " Cannot switch branches when you have unresolved changes".red
|
312
|
+
puts " Use ".red + "'ez switch! <branch>'".red.bold + " to abandon your changes and switch anyway,".red
|
313
|
+
puts " or use ".red + "'ez move <branch>'".red.bold + " to move your changes and switch.".red
|
314
|
+
return
|
315
|
+
end
|
316
|
+
#clobber all files before switching
|
317
|
+
#respect the -f option
|
318
|
+
if has_changes && opts[:switch!]
|
319
|
+
unless opts[:force]
|
320
|
+
display_current_changes
|
321
|
+
puts ''
|
322
|
+
print " WARNING: You may lose changes if you switch branches without committing.".red.bold
|
323
|
+
end
|
324
|
+
return unless run_lambda_with_force_option(opts) do
|
325
|
+
opts[:force] = true
|
326
|
+
x = `git clean -df`
|
327
|
+
x = `git checkout -f #{branch_name}`
|
328
|
+
return
|
329
|
+
end
|
330
|
+
x = `git clean -df`
|
331
|
+
end
|
332
|
+
x = `git checkout -f #{branch_name}`
|
333
|
+
end
|
334
|
+
|
335
|
+
|
336
|
+
def commit(args)
|
337
|
+
return puts "Please specify a message.".yellow.bold if args.count < 1
|
338
|
+
return puts "Invalid number of arguments. Please specify only a message.".yellow.bold if args.count > 1
|
339
|
+
has_changes, changes = check_local_changes
|
340
|
+
return puts "There are no changes to commit".yellow.bold unless has_changes
|
341
|
+
commit_id = args[0].to_s
|
342
|
+
puts `git add -A`
|
343
|
+
puts `git commit -m "#{commit_id}"`
|
344
|
+
end
|
345
|
+
|
346
|
+
|
347
|
+
def fetch
|
348
|
+
stdin, stdout, stderr = Open3.popen3("git fetch -p #{@dry_run_flag}")
|
349
|
+
puts stderr.readlines.join('') + stdout.readlines.join('')
|
350
|
+
refresh_branches
|
351
|
+
end
|
352
|
+
|
353
|
+
|
354
|
+
def pull
|
355
|
+
fetch
|
356
|
+
stat, count = check_remote_status
|
357
|
+
case stat
|
358
|
+
when :rebase
|
359
|
+
if @its_a_dry_run
|
360
|
+
puts 'would merge changes'
|
361
|
+
display_sync_status
|
362
|
+
return
|
363
|
+
end
|
364
|
+
puts `git rebase #{remote_branch}`
|
365
|
+
#TODO: CONFLICT HANDLING?
|
366
|
+
puts 'TODO: CONFLICT HANDLING?'
|
367
|
+
when :behind
|
368
|
+
if @its_a_dry_run
|
369
|
+
puts "would reset branch to #{remote_branch}"
|
370
|
+
display_sync_status
|
371
|
+
return
|
372
|
+
end
|
373
|
+
puts `git reset --hard #{remote_branch}`
|
374
|
+
when :headless
|
375
|
+
puts ' You cannot pull unless you are on a branch.'.red.bold
|
376
|
+
display_sync_status
|
377
|
+
return
|
378
|
+
end
|
379
|
+
info
|
380
|
+
end
|
381
|
+
|
382
|
+
|
383
|
+
def push
|
384
|
+
stat, count = check_remote_status
|
385
|
+
if stat.eql?(:rebase) || stat.eql?(:behind)
|
386
|
+
puts " The remote has been updated since you began this sync.".yellow.bold
|
387
|
+
puts " Try running 'ez pull' again".yellow.bold
|
388
|
+
elsif stat.eql?(:no_remote) || stat.eql?(:ahead)
|
389
|
+
puts `git push -u #{remote_branch.sub('/', ' ')}`
|
390
|
+
elsif stat.eql?(:headless)
|
391
|
+
puts ' You cannot push unless you are on a branch.'.red.bold
|
392
|
+
else
|
393
|
+
#:up_to_date | :headless
|
394
|
+
end
|
395
|
+
info
|
396
|
+
end
|
397
|
+
|
398
|
+
|
399
|
+
end
|