cherrybase 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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +23 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/bin/cherrybase +6 -0
- data/cherrybase.gemspec +71 -0
- data/lib/args.rb +32 -0
- data/lib/baser.rb +78 -0
- data/lib/cmd.rb +21 -0
- data/lib/executor.rb +47 -0
- data/lib/file_util.rb +45 -0
- data/lib/git.rb +118 -0
- data/spec/args_spec.rb +47 -0
- data/spec/baser_spec.rb +201 -0
- data/spec/executor_spec.rb +32 -0
- data/spec/file_util_spec.rb +57 -0
- data/spec/git_spec.rb +156 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +24 -0
- metadata +90 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2009 born2snipe
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
= cherrybase
|
|
2
|
+
|
|
3
|
+
Git cherry-pick a range of commits from a another branch into your current branch
|
|
4
|
+
|
|
5
|
+
== Example usages:
|
|
6
|
+
This will attempt to cherry-pick from the <first-commit> all the way to the very last commit of the <branch>.
|
|
7
|
+
|
|
8
|
+
cherrybase <branch> <first-commit>
|
|
9
|
+
|
|
10
|
+
This will attempt to cherry-pick from the <first-commit> all the way to <last-commit> from the <branch>.
|
|
11
|
+
|
|
12
|
+
cherrybase <branch> <first-commit>..<last-commit>
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
If you encounter a merge conflict you should be able to handle this similar to how you would a 'git rebase'. You make the changes as needed and stage the changes for commit. You now have two choices just like with rebasing '--continue' or '--abort'.
|
|
16
|
+
|
|
17
|
+
This will commit your changes using the authorship of the original commit. Then attempt to proceed with the cherry-picking.
|
|
18
|
+
|
|
19
|
+
cherrybase --continue
|
|
20
|
+
|
|
21
|
+
This will reset your HEAD back to the original commit just before you started cherrybasing.
|
|
22
|
+
|
|
23
|
+
cherrybase --abort
|
data/Rakefile
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require 'jeweler'
|
|
6
|
+
Jeweler::Tasks.new do |gem|
|
|
7
|
+
gem.name = "cherrybase"
|
|
8
|
+
gem.summary = %Q{Ruby gem to cherry-pick a range of commits with similar rebase options}
|
|
9
|
+
gem.description = %Q{Ruby gem to cherry-pick a range of commits with similar rebase options}
|
|
10
|
+
gem.email = "born2snipe@gmail.com"
|
|
11
|
+
gem.homepage = "http://github.com/born2snipe/cherrybase"
|
|
12
|
+
gem.authors = ["born2snipe"]
|
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
|
15
|
+
end
|
|
16
|
+
Jeweler::GemcutterTasks.new
|
|
17
|
+
rescue LoadError
|
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
require 'spec/rake/spectask'
|
|
22
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
|
23
|
+
spec.libs << 'lib' << 'spec'
|
|
24
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
|
28
|
+
spec.libs << 'lib' << 'spec'
|
|
29
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
|
30
|
+
spec.rcov = true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
task :spec => :check_dependencies
|
|
34
|
+
|
|
35
|
+
task :default => :spec
|
|
36
|
+
|
|
37
|
+
require 'rake/rdoctask'
|
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
|
39
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
|
40
|
+
|
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
42
|
+
rdoc.title = "cherrybase #{version}"
|
|
43
|
+
rdoc.rdoc_files.include('README*')
|
|
44
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
45
|
+
end
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.0.1
|
data/bin/cherrybase
ADDED
data/cherrybase.gemspec
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Generated by jeweler
|
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
|
4
|
+
# -*- encoding: utf-8 -*-
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |s|
|
|
7
|
+
s.name = %q{cherrybase}
|
|
8
|
+
s.version = "0.0.1"
|
|
9
|
+
|
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
11
|
+
s.authors = ["born2snipe"]
|
|
12
|
+
s.date = %q{2009-12-12}
|
|
13
|
+
s.default_executable = %q{cherrybase}
|
|
14
|
+
s.description = %q{Ruby gem to cherry-pick a range of commits with similar rebase options}
|
|
15
|
+
s.email = %q{born2snipe@gmail.com}
|
|
16
|
+
s.executables = ["cherrybase"]
|
|
17
|
+
s.extra_rdoc_files = [
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"README.rdoc"
|
|
20
|
+
]
|
|
21
|
+
s.files = [
|
|
22
|
+
".document",
|
|
23
|
+
".gitignore",
|
|
24
|
+
"LICENSE",
|
|
25
|
+
"README.rdoc",
|
|
26
|
+
"Rakefile",
|
|
27
|
+
"VERSION",
|
|
28
|
+
"bin/cherrybase",
|
|
29
|
+
"cherrybase.gemspec",
|
|
30
|
+
"lib/args.rb",
|
|
31
|
+
"lib/baser.rb",
|
|
32
|
+
"lib/cmd.rb",
|
|
33
|
+
"lib/executor.rb",
|
|
34
|
+
"lib/file_util.rb",
|
|
35
|
+
"lib/git.rb",
|
|
36
|
+
"spec/args_spec.rb",
|
|
37
|
+
"spec/baser_spec.rb",
|
|
38
|
+
"spec/executor_spec.rb",
|
|
39
|
+
"spec/file_util_spec.rb",
|
|
40
|
+
"spec/git_spec.rb",
|
|
41
|
+
"spec/spec.opts",
|
|
42
|
+
"spec/spec_helper.rb"
|
|
43
|
+
]
|
|
44
|
+
s.homepage = %q{http://github.com/born2snipe/cherrybase}
|
|
45
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
|
46
|
+
s.require_paths = ["lib"]
|
|
47
|
+
s.rubygems_version = %q{1.3.5}
|
|
48
|
+
s.summary = %q{Ruby gem to cherry-pick a range of commits with similar rebase options}
|
|
49
|
+
s.test_files = [
|
|
50
|
+
"spec/args_spec.rb",
|
|
51
|
+
"spec/baser_spec.rb",
|
|
52
|
+
"spec/executor_spec.rb",
|
|
53
|
+
"spec/file_util_spec.rb",
|
|
54
|
+
"spec/git_spec.rb",
|
|
55
|
+
"spec/spec_helper.rb"
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
if s.respond_to? :specification_version then
|
|
59
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
|
60
|
+
s.specification_version = 3
|
|
61
|
+
|
|
62
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
|
63
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
|
64
|
+
else
|
|
65
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
|
66
|
+
end
|
|
67
|
+
else
|
|
68
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
data/lib/args.rb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Cherrybase
|
|
2
|
+
class Args
|
|
3
|
+
def parse(args)
|
|
4
|
+
if args.include?('--help')
|
|
5
|
+
return {'help' => true}
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
result = {}
|
|
9
|
+
|
|
10
|
+
if !args.include?('--continue') && !args.include?('--abort')
|
|
11
|
+
raise "You must supply at least the branch name and the starting commit hash to begin cherrybasing" if args.length != 2
|
|
12
|
+
|
|
13
|
+
result['branch'] = args[0]
|
|
14
|
+
|
|
15
|
+
if args[1] =~ /[0-9a-z]+\.\.[0-9a-z]+/
|
|
16
|
+
parts = args[1].split('..')
|
|
17
|
+
result['starting-commit'] = parts[0]
|
|
18
|
+
result['ending-commit'] = parts[1]
|
|
19
|
+
else
|
|
20
|
+
result['starting-commit'] = args[1]
|
|
21
|
+
end
|
|
22
|
+
else
|
|
23
|
+
result['continue'] = args.include?('--continue')
|
|
24
|
+
result['abort'] = args.include?('--abort')
|
|
25
|
+
|
|
26
|
+
raise "You supplied --abort and --continue, please pick one" if result['continue'] && result['abort']
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
result
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/baser.rb
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
require 'git'
|
|
2
|
+
require 'file_util'
|
|
3
|
+
|
|
4
|
+
module Cherrybase
|
|
5
|
+
class Baser
|
|
6
|
+
|
|
7
|
+
def initialize(git = Cherrybase::Git.new, file_util = Cherrybase::FileUtil.new)
|
|
8
|
+
@git = git
|
|
9
|
+
@file_util = file_util
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def init(branch_name, starting_commit, ending_commit)
|
|
13
|
+
raise "Could not locate .git folder! Is this a Git repository?" if !@file_util.git_repo?
|
|
14
|
+
raise "Could not find branch (#{branch_name}) in the Git repository" if !@git.has_branch?(branch_name)
|
|
15
|
+
raise "It appears you are already in the middle of a cherrybase!?" if @file_util.temp_file?
|
|
16
|
+
raise "Could not locate START hash (#{starting_commit}) in the Git repository history" if !@git.has_commit?(branch_name, starting_commit)
|
|
17
|
+
raise "Could not locate END hash (#{ending_commit}) in the Git repository history" if ending_commit != nil && !@git.has_commit?(branch_name, ending_commit)
|
|
18
|
+
|
|
19
|
+
first_commit = @git.resolve_commit(branch_name, starting_commit)
|
|
20
|
+
if (ending_commit)
|
|
21
|
+
last_commit = @git.resolve_commit(branch_name, ending_commit)
|
|
22
|
+
else
|
|
23
|
+
last_commit = @git.last_commit(branch_name)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
commits = @git.commits_to_cherrypick(branch_name, first_commit, last_commit)
|
|
27
|
+
@file_util.write_temp_file(@git.last_commit(@git.current_branch), first_commit, commits)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def continue(commit_previous_hash = false)
|
|
31
|
+
raise "It appears you are not in the middle of a cherrybase!?" if !@file_util.temp_file?
|
|
32
|
+
|
|
33
|
+
temp_data = @file_util.read_temp_file()
|
|
34
|
+
commits = temp_data['commits']
|
|
35
|
+
next_cherrypick = temp_data['next_cherrypick']
|
|
36
|
+
|
|
37
|
+
if commit_previous_hash
|
|
38
|
+
@git.commit(commits[commits.index(next_cherrypick) - 1])
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
conflicts_found = false
|
|
42
|
+
last_commit_applied = nil
|
|
43
|
+
i = commits.index(next_cherrypick)
|
|
44
|
+
|
|
45
|
+
while i < commits.length
|
|
46
|
+
puts "Applying #{i+1} of #{commits.length} cherry-picks\r"
|
|
47
|
+
last_commit_applied = commits[i]
|
|
48
|
+
@git.cherry_pick(last_commit_applied)
|
|
49
|
+
if @git.has_conflicts?
|
|
50
|
+
conflicts_found = true
|
|
51
|
+
break
|
|
52
|
+
end
|
|
53
|
+
i += 1
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
if conflicts_found
|
|
57
|
+
puts "Conflict(s) Encountered!"
|
|
58
|
+
@git.status
|
|
59
|
+
if (last_commit_applied == commits.last)
|
|
60
|
+
@file_util.delete_temp_file()
|
|
61
|
+
else
|
|
62
|
+
@file_util.write_temp_file(temp_data['starting_commit'], commits.last, commits)
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
@file_util.delete_temp_file()
|
|
66
|
+
end
|
|
67
|
+
puts "Cherrybase completed!"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def abort()
|
|
71
|
+
raise "It appears you are not in the middle of a cherrybase!?" if !@file_util.temp_file?
|
|
72
|
+
temp_data = @file_util.read_temp_file()
|
|
73
|
+
@git.reset(temp_data['starting_commit'])
|
|
74
|
+
@file_util.delete_temp_file()
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
end
|
data/lib/cmd.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'open3'
|
|
2
|
+
|
|
3
|
+
module Cherrybase
|
|
4
|
+
DEBUG = false
|
|
5
|
+
|
|
6
|
+
class Cmd
|
|
7
|
+
def run(command = '', show_lines = false)
|
|
8
|
+
if DEBUG
|
|
9
|
+
puts "[Cmd::run] #{command}"
|
|
10
|
+
end
|
|
11
|
+
lines = []
|
|
12
|
+
Open3.popen3(command) { |stdin, stdout, stderr| lines = stdout.readlines }
|
|
13
|
+
if DEBUG || show_lines
|
|
14
|
+
lines.each do |line|
|
|
15
|
+
puts line
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
lines
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/executor.rb
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'args'
|
|
2
|
+
require 'baser'
|
|
3
|
+
|
|
4
|
+
module Cherrybase
|
|
5
|
+
class Executor
|
|
6
|
+
def initialize(baser = Cherrybase::Baser.new)
|
|
7
|
+
@baser = baser
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def execute(args)
|
|
11
|
+
begin
|
|
12
|
+
input = Cherrybase::Args.new().parse(args)
|
|
13
|
+
if (input['continue'])
|
|
14
|
+
@baser.continue(true)
|
|
15
|
+
else
|
|
16
|
+
if (input['abort'])
|
|
17
|
+
@baser.abort
|
|
18
|
+
else
|
|
19
|
+
if input['help']
|
|
20
|
+
showHelp()
|
|
21
|
+
else
|
|
22
|
+
@baser.init(input['branch'], input['starting-commit'], input['ending-commit'])
|
|
23
|
+
@baser.continue
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
rescue RuntimeError => err
|
|
28
|
+
puts "\n#{err}\n\n"
|
|
29
|
+
showUsage()
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def showUsage()
|
|
34
|
+
puts "Usage: cherrybase [<branch> [<commit> | <commit>..<commit>]] | --continue | --abort"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def showHelp()
|
|
38
|
+
puts "NAME"
|
|
39
|
+
puts "\tcherrybase - cherry-pick a range of commits from one branch to the current branch"
|
|
40
|
+
puts "SYNOPSIS"
|
|
41
|
+
puts "\tcherrybase [<branch> [<commit> | <commit>..<commit>]] | --continue | --abort"
|
|
42
|
+
puts "DESCRIPTION"
|
|
43
|
+
puts "\tThe idea is to cherry-pick across multiple commits and have similar functionality as the rebase command."
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
end
|
data/lib/file_util.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
|
|
3
|
+
module Cherrybase
|
|
4
|
+
class FileUtil
|
|
5
|
+
def git_repo?(directory = File.expand_path('.'))
|
|
6
|
+
git_root_dir(directory) != nil
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def git_root_dir(directory = File.expand_path('.'))
|
|
10
|
+
current_directory = directory
|
|
11
|
+
while !File.exists?(File.join(current_directory, '.git'))
|
|
12
|
+
current_directory = File.dirname(current_directory)
|
|
13
|
+
end
|
|
14
|
+
current_directory
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def temp_file?(directory = File.expand_path('.'))
|
|
18
|
+
File.exist?(temp_file(directory))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def temp_file(directory = File.expand_path('.'))
|
|
22
|
+
File.join(File.join(git_root_dir(directory), '.git'), 'cherrybase')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def delete_temp_file(directory = File.expand_path('.'))
|
|
26
|
+
File.delete(temp_file(directory))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def read_temp_file(directory = File.expand_path('.'))
|
|
30
|
+
YAML::load_file( temp_file(directory) )
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def write_temp_file(starting_commit = nil, next_cherrypick = nil, commits = nil, directory = File.expand_path('.'))
|
|
34
|
+
data = {
|
|
35
|
+
"starting_commit" => starting_commit,
|
|
36
|
+
"next_cherrypick" => next_cherrypick,
|
|
37
|
+
"commits" => commits
|
|
38
|
+
}
|
|
39
|
+
filename = temp_file(directory)
|
|
40
|
+
File.open(filename, "w") do |f|
|
|
41
|
+
f.write(YAML::dump(data))
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/git.rb
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
require 'cmd'
|
|
2
|
+
|
|
3
|
+
module Cherrybase
|
|
4
|
+
class Git
|
|
5
|
+
def initialize(cmd = Cherrybase::Cmd.new)
|
|
6
|
+
@cmd = cmd
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def reset(commit_hash)
|
|
10
|
+
@cmd.run("git reset --hard #{commit_hash}")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def has_commit?(branch_name, commit_hash)
|
|
14
|
+
resolve_commit(branch_name, commit_hash) != nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def resolve_commit(branch_name, commit_hash)
|
|
18
|
+
if commit_hash
|
|
19
|
+
raise "Please supply at least 5 characters for a commit hash" if commit_hash.length < 5
|
|
20
|
+
@cmd.run("git log #{branch_name} --pretty=oneline").each do |line|
|
|
21
|
+
if line == commit_hash || line.include?(commit_hash)
|
|
22
|
+
return line.split(' ')[0]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def last_commit(branch_name)
|
|
30
|
+
lines = @cmd.run("git log #{branch_name} --pretty=oneline")
|
|
31
|
+
if (lines.length > 0)
|
|
32
|
+
lines[0].split(' ')[0]
|
|
33
|
+
else
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def cherry_pick(commit_hash)
|
|
39
|
+
@cmd.run("git cherry-pick #{commit_hash}")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def has_conflicts?()
|
|
43
|
+
@cmd.run("git ls-files -tu").length > 0
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def commit(commit_hash)
|
|
47
|
+
@cmd.run("git commit -C #{commit_hash}")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def status()
|
|
51
|
+
@cmd.run("git status", true)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def current_branch()
|
|
55
|
+
@cmd.run("git branch").each do |line|
|
|
56
|
+
if line.index('*')
|
|
57
|
+
return line.gsub(/\*(.+)/, '\1').strip
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def has_branch?(branch_name)
|
|
64
|
+
@cmd.run("git branch").each do |line|
|
|
65
|
+
if line.strip == branch_name
|
|
66
|
+
return true
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
false
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def commits_to_cherrypick(branch_name, first_commit = nil, last_commit = nil)
|
|
73
|
+
commits = []
|
|
74
|
+
@cmd.run("git log #{branch_name} --pretty=oneline").each do |line|
|
|
75
|
+
commit_hash = line.split(' ')[0]
|
|
76
|
+
if commit_hash == first_commit
|
|
77
|
+
commits << commit_hash
|
|
78
|
+
break
|
|
79
|
+
else
|
|
80
|
+
commits << commit_hash
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
if last_commit
|
|
85
|
+
remove_commits = []
|
|
86
|
+
commits.each do |commit|
|
|
87
|
+
if commit == last_commit
|
|
88
|
+
break
|
|
89
|
+
else
|
|
90
|
+
remove_commits << commit
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
commits = commits - remove_commits
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
commits.reverse
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def last_svn_commit()
|
|
100
|
+
last_commit_hash = nil
|
|
101
|
+
svn_commit_found = false
|
|
102
|
+
@cmd.run("git log").each do |line|
|
|
103
|
+
if line.include?("git-svn-id")
|
|
104
|
+
svn_commit_found = true
|
|
105
|
+
break
|
|
106
|
+
else
|
|
107
|
+
if line =~ /commit [a-z0-9]+$/
|
|
108
|
+
last_commit_hash = line[7,line.length]
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
if (!svn_commit_found)
|
|
113
|
+
last_commit_hash = nil
|
|
114
|
+
end
|
|
115
|
+
last_commit_hash
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
data/spec/args_spec.rb
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
|
+
|
|
3
|
+
describe Cherrybase::Args do
|
|
4
|
+
|
|
5
|
+
before(:each) do
|
|
6
|
+
@args = Cherrybase::Args.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "should show help" do
|
|
10
|
+
@args.parse(['--help']).should == {'help' => true}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "should break up a range of commits and set the starting and the last commit" do
|
|
14
|
+
@args.parse(['branch', 'start..end']).should == {'branch'=>'branch', 'starting-commit' => 'start', 'ending-commit' => 'end'}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should raise an error if a branch name is given without a commit hash" do
|
|
18
|
+
lambda {
|
|
19
|
+
@args.parse(['branch-name'])
|
|
20
|
+
}.should raise_error(RuntimeError, 'You must supply at least the branch name and the starting commit hash to begin cherrybasing')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "should set the branch name and the commit hash given" do
|
|
24
|
+
@args.parse(['branch-name', 'commit-hash']).should == {'branch' => 'branch-name', 'starting-commit' => 'commit-hash'}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should raise an error if a commit hash is not given" do
|
|
28
|
+
lambda {
|
|
29
|
+
@args.parse([])
|
|
30
|
+
}.should raise_error(RuntimeError, 'You must supply at least the branch name and the starting commit hash to begin cherrybasing')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should raise an error if both --abort and --continue are given" do
|
|
34
|
+
lambda {
|
|
35
|
+
@args.parse(['--abort', '--continue'])
|
|
36
|
+
}.should raise_error(RuntimeError, "You supplied --abort and --continue, please pick one")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "should set abort to true if --abort is given" do
|
|
40
|
+
@args.parse(['--abort']).should == {'abort' => true, 'continue' => false}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should set continue to true if --continue is given" do
|
|
44
|
+
@args.parse(['--continue']).should == {'continue' => true, 'abort' => false}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
data/spec/baser_spec.rb
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
|
+
|
|
3
|
+
describe Cherrybase::Baser do
|
|
4
|
+
BRANCH = 'branch-name'
|
|
5
|
+
|
|
6
|
+
before(:each) do
|
|
7
|
+
@git = mock("git")
|
|
8
|
+
@file_util = mock("file_util")
|
|
9
|
+
@baser = Cherrybase::Baser.new(@git, @file_util)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "should reset HEAD back to the last original commit before any cherry-picks" do
|
|
13
|
+
@file_util.stub!(:temp_file?).and_return(true)
|
|
14
|
+
@file_util.stub!(:read_temp_file).and_return({"starting_commit" => "start"})
|
|
15
|
+
@file_util.should_receive(:delete_temp_file)
|
|
16
|
+
@git.should_receive(:reset).with("start")
|
|
17
|
+
|
|
18
|
+
@baser.abort
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should raise an error if you try to abort while not in a cherrybase" do
|
|
22
|
+
@file_util.should_receive(:temp_file?).and_return(false)
|
|
23
|
+
|
|
24
|
+
lambda {
|
|
25
|
+
@baser.abort
|
|
26
|
+
}.should raise_error(RuntimeError, "It appears you are not in the middle of a cherrybase!?")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "should use the end commit if given" do
|
|
30
|
+
@file_util.should_receive(:git_repo?).and_return(true)
|
|
31
|
+
@file_util.should_receive(:temp_file?).and_return(false)
|
|
32
|
+
@git.should_receive(:has_branch?).with(BRANCH).and_return(true)
|
|
33
|
+
@git.should_receive(:has_commit?).with(BRANCH, 'starting-commit').and_return(true)
|
|
34
|
+
@git.should_receive(:has_commit?).with(BRANCH, 'end-commit').and_return(true)
|
|
35
|
+
@git.should_receive(:commits_to_cherrypick).with(BRANCH, 'first_commit', 'last_commit').and_return(['commits-to-cherrypick'])
|
|
36
|
+
@git.stub!(:resolve_commit).with(BRANCH, "starting-commit").and_return("first_commit")
|
|
37
|
+
@git.stub!(:resolve_commit).with(BRANCH, "end-commit").and_return("last_commit")
|
|
38
|
+
@file_util.should_receive(:write_temp_file).with('last_original_commit', 'first_commit', ['commits-to-cherrypick'])
|
|
39
|
+
@git.stub!(:current_branch).and_return("current")
|
|
40
|
+
@git.stub!(:last_commit).with("current").and_return("last_original_commit")
|
|
41
|
+
|
|
42
|
+
@baser.init(BRANCH, 'starting-commit', "end-commit")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "should raise an error if the end commit could not be located in the history" do
|
|
46
|
+
@file_util.should_receive(:git_repo?).and_return(true)
|
|
47
|
+
@file_util.should_receive(:temp_file?).and_return(false)
|
|
48
|
+
@git.should_receive(:has_branch?).with(BRANCH).and_return(true)
|
|
49
|
+
@git.should_receive(:has_commit?).with(BRANCH, "start").and_return(true)
|
|
50
|
+
@git.should_receive(:has_commit?).with(BRANCH, "end").and_return(false)
|
|
51
|
+
lambda {
|
|
52
|
+
@baser.init(BRANCH, "start", "end")
|
|
53
|
+
}.should raise_error(RuntimeError, "Could not locate END hash (end) in the Git repository history")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "should raise an error if the start commit could not be located in the history" do
|
|
57
|
+
@file_util.should_receive(:git_repo?).and_return(true)
|
|
58
|
+
@file_util.should_receive(:temp_file?).and_return(false)
|
|
59
|
+
@git.should_receive(:has_branch?).with(BRANCH).and_return(true)
|
|
60
|
+
lambda {
|
|
61
|
+
@git.should_receive(:has_commit?).with(BRANCH, "doesNotExist").and_return(false)
|
|
62
|
+
@baser.init(BRANCH, "doesNotExist", nil)
|
|
63
|
+
}.should raise_error(RuntimeError, "Could not locate START hash (doesNotExist) in the Git repository history")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "should commit staged merge resolution" do
|
|
67
|
+
@file_util.should_receive(:temp_file?).and_return(true)
|
|
68
|
+
@file_util.should_receive(:read_temp_file).and_return({
|
|
69
|
+
"starting_commit" => "start",
|
|
70
|
+
"next_cherrypick" => "commit1",
|
|
71
|
+
"commits" => ["start", "commit1"]
|
|
72
|
+
})
|
|
73
|
+
@git.should_receive(:commit).with("start")
|
|
74
|
+
@git.should_receive(:cherry_pick).with("commit1")
|
|
75
|
+
@git.should_receive(:has_conflicts?).and_return(false)
|
|
76
|
+
@file_util.should_receive(:delete_temp_file)
|
|
77
|
+
|
|
78
|
+
@baser.continue(true)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "should start apply commits based on the next_cherrypick" do
|
|
82
|
+
@file_util.should_receive(:temp_file?).and_return(true)
|
|
83
|
+
@file_util.should_receive(:read_temp_file).and_return({
|
|
84
|
+
"starting_commit" => "start",
|
|
85
|
+
"next_cherrypick" => "commit1",
|
|
86
|
+
"commits" => ["start", "commit1"]
|
|
87
|
+
})
|
|
88
|
+
@git.should_receive(:cherry_pick).with("commit1")
|
|
89
|
+
@git.should_receive(:has_conflicts?).and_return(false)
|
|
90
|
+
@file_util.should_receive(:delete_temp_file)
|
|
91
|
+
|
|
92
|
+
@baser.continue
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should cleanup the temp file if a conflict is encountered on the last commit" do
|
|
96
|
+
@file_util.should_receive(:temp_file?).and_return(true)
|
|
97
|
+
@file_util.should_receive(:read_temp_file).and_return({
|
|
98
|
+
"starting_commit" => "start",
|
|
99
|
+
"next_cherrypick" => "start",
|
|
100
|
+
"commits" => ["start"]
|
|
101
|
+
})
|
|
102
|
+
@git.should_receive(:cherry_pick).with("start")
|
|
103
|
+
@git.should_receive(:has_conflicts?).and_return(true)
|
|
104
|
+
@git.should_receive(:status)
|
|
105
|
+
@file_util.should_receive(:delete_temp_file)
|
|
106
|
+
|
|
107
|
+
@baser.continue
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "should stop cherrypicking if a conflict is found" do
|
|
111
|
+
@file_util.should_receive(:temp_file?).and_return(true)
|
|
112
|
+
@file_util.should_receive(:read_temp_file).and_return({
|
|
113
|
+
"starting_commit" => "start",
|
|
114
|
+
"next_cherrypick" => "start",
|
|
115
|
+
"commits" => ["start", "end"]
|
|
116
|
+
})
|
|
117
|
+
@git.should_receive(:cherry_pick).with("start")
|
|
118
|
+
@git.should_receive(:has_conflicts?).and_return(true)
|
|
119
|
+
@git.should_receive(:status)
|
|
120
|
+
@file_util.should_receive(:write_temp_file).with("start", "end", ["start", "end"])
|
|
121
|
+
|
|
122
|
+
@baser.continue
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
it "should attempt to cherry-pick all the commits left (two commits)" do
|
|
127
|
+
@file_util.should_receive(:temp_file?).and_return(true)
|
|
128
|
+
@file_util.should_receive(:read_temp_file).and_return({
|
|
129
|
+
"starting_commit" => "start",
|
|
130
|
+
"next_cherrypick" => "start",
|
|
131
|
+
"commits" => ["start", "end"]
|
|
132
|
+
})
|
|
133
|
+
@git.should_receive(:cherry_pick).with("start")
|
|
134
|
+
@git.should_receive(:cherry_pick).with("end")
|
|
135
|
+
@git.should_receive(:has_conflicts?).and_return(false)
|
|
136
|
+
@git.should_receive(:has_conflicts?).and_return(false)
|
|
137
|
+
@file_util.should_receive(:delete_temp_file)
|
|
138
|
+
|
|
139
|
+
@baser.continue
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it "should attempt to cherry-pick all the commits left (one commit)" do
|
|
143
|
+
@file_util.should_receive(:temp_file?).and_return(true)
|
|
144
|
+
@file_util.should_receive(:read_temp_file).and_return({
|
|
145
|
+
"starting_commit" => "start",
|
|
146
|
+
"next_cherrypick" => "start",
|
|
147
|
+
"commits" => ["start"]
|
|
148
|
+
})
|
|
149
|
+
@git.should_receive(:cherry_pick).with("start")
|
|
150
|
+
@git.should_receive(:has_conflicts?).and_return(false)
|
|
151
|
+
@file_util.should_receive(:delete_temp_file)
|
|
152
|
+
|
|
153
|
+
@baser.continue
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it "should throw an error if you are not in the middle of a cherrybase" do
|
|
157
|
+
@file_util.should_receive(:temp_file?).and_return(false)
|
|
158
|
+
lambda {
|
|
159
|
+
@baser.continue
|
|
160
|
+
}.should raise_error(RuntimeError, "It appears you are not in the middle of a cherrybase!?")
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
it "should create the cherrybase temp file with the given branch's last commit" do
|
|
164
|
+
@file_util.should_receive(:git_repo?).and_return(true)
|
|
165
|
+
@file_util.should_receive(:temp_file?).and_return(false)
|
|
166
|
+
@git.should_receive(:has_branch?).with(BRANCH).and_return(true)
|
|
167
|
+
@git.should_receive(:last_commit).with(BRANCH).and_return('last-commit')
|
|
168
|
+
@git.should_receive(:has_commit?).with(BRANCH, 'starting-commit').and_return(true)
|
|
169
|
+
@git.should_receive(:commits_to_cherrypick).with(BRANCH, 'first_commit', 'last-commit').and_return(['commits-to-cherrypick'])
|
|
170
|
+
@git.stub!(:resolve_commit).with(BRANCH, "starting-commit").and_return("first_commit")
|
|
171
|
+
@git.stub!(:current_branch).and_return("current")
|
|
172
|
+
@git.stub!(:last_commit).with("current").and_return("last_original_commit")
|
|
173
|
+
@file_util.should_receive(:write_temp_file).with('last_original_commit', 'first_commit', ['commits-to-cherrypick'])
|
|
174
|
+
@baser.init(BRANCH, 'starting-commit', nil)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "should throw an error if the given branch name does not exist in the repository" do
|
|
178
|
+
lambda {
|
|
179
|
+
@file_util.should_receive(:git_repo?).and_return(true)
|
|
180
|
+
@git.should_receive(:has_branch?).with(BRANCH).and_return(false)
|
|
181
|
+
@baser.init(BRANCH, nil, nil)
|
|
182
|
+
}.should raise_error(RuntimeError, "Could not find branch (branch-name) in the Git repository")
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
it "should throw an exception if the git repo folder could not be discovered" do
|
|
186
|
+
lambda {
|
|
187
|
+
@file_util.should_receive(:git_repo?).and_return(false)
|
|
188
|
+
@baser.init(nil, nil, nil)
|
|
189
|
+
}.should raise_error(RuntimeError, "Could not locate .git folder! Is this a Git repository?")
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it "should throw an error if you already in the middle of a cherrybase" do
|
|
193
|
+
@file_util.should_receive(:git_repo?).and_return(true)
|
|
194
|
+
@git.should_receive(:has_branch?).with(BRANCH).and_return(true)
|
|
195
|
+
lambda {
|
|
196
|
+
@file_util.should_receive(:temp_file?).and_return(true)
|
|
197
|
+
@baser.init("branch-name", nil, nil)
|
|
198
|
+
}.should raise_error(RuntimeError, "It appears you are already in the middle of a cherrybase!?")
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
|
+
|
|
3
|
+
describe Cherrybase::Executor do
|
|
4
|
+
|
|
5
|
+
before(:each) do
|
|
6
|
+
@baser = mock('baser')
|
|
7
|
+
@executor = Cherrybase::Executor.new(@baser)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "should tell the baser to init with the appropriate branch name and range of commits" do
|
|
11
|
+
@baser.should_receive(:init).with('branch-name', 'starting-commit', 'ending-commit')
|
|
12
|
+
@baser.should_receive(:continue)
|
|
13
|
+
@executor.execute(['branch-name', 'starting-commit..ending-commit'])
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "should tell the baser to init with the appropriate branch and starting commit" do
|
|
17
|
+
@baser.should_receive(:init).with('branch-name', 'starting-commit', nil)
|
|
18
|
+
@baser.should_receive(:continue)
|
|
19
|
+
@executor.execute(['branch-name', 'starting-commit'])
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "should tell the baser to abort" do
|
|
23
|
+
@baser.should_receive(:abort)
|
|
24
|
+
@executor.execute(['--abort'])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should tell the baser to continue to the next commit" do
|
|
28
|
+
@baser.should_receive(:continue).with(true)
|
|
29
|
+
@executor.execute(['--continue'])
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
|
+
|
|
3
|
+
describe Cherrybase::FileUtil do
|
|
4
|
+
|
|
5
|
+
before(:each) do
|
|
6
|
+
@fixtures_dir = File.expand_path(File.join(File.join(__FILE__, ".."), ".."), "fixtures")
|
|
7
|
+
@project = File.join(File.join(@fixtures_dir, "project"))
|
|
8
|
+
@file_util = Cherrybase::FileUtil.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "should delete the temp file" do
|
|
12
|
+
test_project = File.join(@fixtures_dir, 'cherrybase-inprogress')
|
|
13
|
+
@file_util.write_temp_file("starting-commit", "next-commit", ["commit1", "commit2"], test_project)
|
|
14
|
+
|
|
15
|
+
@file_util.delete_temp_file(test_project)
|
|
16
|
+
@file_util.temp_file?(test_project).should == false
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "should read the temp file" do
|
|
20
|
+
test_project = File.join(@fixtures_dir, 'cherrybase-inprogress')
|
|
21
|
+
@file_util.write_temp_file("starting-commit", "next-commit", ["commit1", "commit2"], test_project)
|
|
22
|
+
@file_util.read_temp_file(test_project).should == {
|
|
23
|
+
"starting_commit" => "starting-commit",
|
|
24
|
+
"next_cherrypick" => "next-commit",
|
|
25
|
+
"commits" => ["commit1", "commit2"]
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "should return nil if the temp file does not exist in the .git folder" do
|
|
30
|
+
expected_tempfile = File.join(File.join(@project, '.git'), 'cherrybase')
|
|
31
|
+
@file_util.temp_file?(@project).should == false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "should find the temp file if it exists in the .git folder" do
|
|
35
|
+
test_project = File.join(@fixtures_dir, 'cherrybase-inprogress')
|
|
36
|
+
@file_util.write_temp_file("starting-commit", "next-commit", ["commit1", "commit2"], test_project)
|
|
37
|
+
expected_tempfile = File.join(File.join(test_project, '.git'), 'cherrybase')
|
|
38
|
+
@file_util.temp_file?(test_project).should == true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should recursively look up the directory tree and return the project directory" do
|
|
42
|
+
@file_util.git_root_dir(File.join(@project, "module")).should == @project
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "should return the current directory if the current directory contains the .git folder" do
|
|
46
|
+
@file_util.git_root_dir(@project).should == @project
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "should recursively look up directory tree for a git repository" do
|
|
50
|
+
@file_util.git_repo?(File.join(@project, "module")).should == true
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should find git repo if in the git main dir" do
|
|
54
|
+
@file_util.git_repo?(@project).should == true
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
data/spec/git_spec.rb
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
|
+
|
|
3
|
+
describe Cherrybase::Git do
|
|
4
|
+
|
|
5
|
+
before(:each) do
|
|
6
|
+
@cmd = mock("cmd")
|
|
7
|
+
@git = Cherrybase::Git.new(@cmd)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "should reset the HEAD to the given commit" do
|
|
11
|
+
@cmd.should_receive(:run).with("git reset --hard commit")
|
|
12
|
+
|
|
13
|
+
@git.reset("commit")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "should raise an error if the commit hash given is not at least 5 characters" do
|
|
17
|
+
lambda {
|
|
18
|
+
@git.has_commit?("branch", "1234")
|
|
19
|
+
}.should raise_error(RuntimeError, "Please supply at least 5 characters for a commit hash")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "should return true if a partial match is found for the given hash" do
|
|
23
|
+
@cmd.should_receive(:run).with("git log branch --pretty=oneline").and_return(["commit1", "commit2-hash", "commit3"])
|
|
24
|
+
@git.has_commit?("branch", "-hash").should == true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should return true if an exact match is found for the given hash" do
|
|
28
|
+
@cmd.should_receive(:run).with("git log branch --pretty=oneline").and_return(["commit"])
|
|
29
|
+
@git.has_commit?("branch", "commit").should == true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should return false if a hash could not be found in the history of the given branch" do
|
|
33
|
+
@cmd.should_receive(:run).with("git log branch --pretty=oneline").and_return([])
|
|
34
|
+
@git.has_commit?("branch", "commit").should == false
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "should return nil if there is no commit history" do
|
|
38
|
+
@cmd.should_receive(:run).with("git log branch --pretty=oneline").and_return([])
|
|
39
|
+
@git.last_commit('branch').should == nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "should return the last commit of the given branch" do
|
|
43
|
+
pretty_log_lines = ["hash1 comment-1", "hash2 comment-2", "hash3 comment-3"]
|
|
44
|
+
@cmd.should_receive(:run).with("git log branch --pretty=oneline").and_return(pretty_log_lines)
|
|
45
|
+
@git.last_commit('branch').should == 'hash1'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should return the current branch name" do
|
|
49
|
+
@cmd.should_receive(:run).with("git branch").and_return(["branch1", "* branch2"])
|
|
50
|
+
@git.current_branch().should == "branch2"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should return false if the branch does not exist" do
|
|
54
|
+
@cmd.should_receive(:run).with("git branch").and_return(["branch1", "branch2"])
|
|
55
|
+
@git.has_branch?("doesNotExist").should == false
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "should return true if the branch exists" do
|
|
59
|
+
@cmd.should_receive(:run).with("git branch").and_return([" branch1", " branch2"])
|
|
60
|
+
@git.has_branch?("branch2").should == true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "should cherry-pick the given commit hash" do
|
|
64
|
+
commit_hash = "commit hash"
|
|
65
|
+
@cmd.should_receive(:run).with("git cherry-pick #{commit_hash}")
|
|
66
|
+
@git.cherry_pick(commit_hash)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "should return false if no files are marked as unmerged" do
|
|
70
|
+
@cmd.should_receive(:run).with("git ls-files -tu").and_return([])
|
|
71
|
+
|
|
72
|
+
@git.has_conflicts?().should == false
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "should return true if files are marked as unmerged" do
|
|
76
|
+
log_lines = [
|
|
77
|
+
"M 100644 e01079f2c38b76cf43780a2899c3f5bd2f50b3a7 1 readme.txt",
|
|
78
|
+
"M 100644 f7b5ff223f06fb323463b81b08759cf13678fd27 2 readme.txt",
|
|
79
|
+
"M 100644 0ebe257230ddb66e12610ad9b304c7605b61dfeb 3 readme.txt"
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
@cmd.should_receive(:run).with("git ls-files -tu").and_return(log_lines)
|
|
83
|
+
|
|
84
|
+
@git.has_conflicts?().should == true
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "should shell out a commit" do
|
|
88
|
+
commit_hash = "commit hash"
|
|
89
|
+
|
|
90
|
+
@cmd.should_receive(:run).with("git commit -C #{commit_hash}")
|
|
91
|
+
|
|
92
|
+
@git.commit(commit_hash)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should shell out a git status" do
|
|
96
|
+
@cmd.should_receive(:run).with("git status", true)
|
|
97
|
+
|
|
98
|
+
@git.status
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "should grab all commit hashes after the commit and the last commit given" do
|
|
102
|
+
pretty_log_lines = ["hash1 comment-1", "hash2 comment-2", "hash3 comment-3", "hash4 commit-4"]
|
|
103
|
+
|
|
104
|
+
@cmd.stub!(:run).with("git log branch --pretty=oneline").and_return(pretty_log_lines)
|
|
105
|
+
|
|
106
|
+
@git.commits_to_cherrypick("branch", "hash3", "hash2").should == ["hash3", "hash2"]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "should grab all commit hashes after the commit and the commit given" do
|
|
110
|
+
pretty_log_lines = ["hash1 comment-1", "hash2 comment-2", "hash3 comment-3"]
|
|
111
|
+
|
|
112
|
+
@cmd.stub!(:run).with("git log branch --pretty=oneline").and_return(pretty_log_lines)
|
|
113
|
+
|
|
114
|
+
@git.commits_to_cherrypick("branch", "hash2").should == ["hash2", "hash1"]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
it "should return nil if no svn commit was found" do
|
|
119
|
+
log_lines = [
|
|
120
|
+
"commit hash1",
|
|
121
|
+
"Author: author-1",
|
|
122
|
+
"Date: date-1",
|
|
123
|
+
"",
|
|
124
|
+
" comment-1",
|
|
125
|
+
""
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
@cmd.stub!(:run).and_return(log_lines)
|
|
129
|
+
|
|
130
|
+
@git.last_svn_commit.should == nil
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "should find the last svn commit hash" do
|
|
134
|
+
log_lines = [
|
|
135
|
+
"commit hash1",
|
|
136
|
+
"Author: author-1",
|
|
137
|
+
"Date: date-1",
|
|
138
|
+
"",
|
|
139
|
+
" comment-1",
|
|
140
|
+
"",
|
|
141
|
+
"commit hash2",
|
|
142
|
+
"Author: author-2",
|
|
143
|
+
"Date: date-2",
|
|
144
|
+
"",
|
|
145
|
+
"comment-2",
|
|
146
|
+
"",
|
|
147
|
+
" git-svn-id: url",
|
|
148
|
+
""
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
@cmd.stub!(:run).and_return(log_lines)
|
|
152
|
+
|
|
153
|
+
@git.last_svn_commit.should == "hash2"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
end
|
data/spec/spec.opts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
3
|
+
require 'git'
|
|
4
|
+
require 'baser'
|
|
5
|
+
require 'file_util'
|
|
6
|
+
require 'args'
|
|
7
|
+
require 'executor'
|
|
8
|
+
|
|
9
|
+
require 'spec'
|
|
10
|
+
require 'spec/autorun'
|
|
11
|
+
|
|
12
|
+
Spec::Runner.configure do |config|
|
|
13
|
+
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module Kernel
|
|
17
|
+
if ENV.keys.find {|env_var| env_var.match(/^TM_/)}
|
|
18
|
+
def rputs(*args)
|
|
19
|
+
puts( *["<pre>", args.collect {|a| CGI.escapeHTML(a.to_s)}, "</pre>"])
|
|
20
|
+
end
|
|
21
|
+
else
|
|
22
|
+
alias_method :rputs, :puts
|
|
23
|
+
end
|
|
24
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: cherrybase
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- born2snipe
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2009-12-12 00:00:00 -06:00
|
|
13
|
+
default_executable: cherrybase
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: rspec
|
|
17
|
+
type: :development
|
|
18
|
+
version_requirement:
|
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
20
|
+
requirements:
|
|
21
|
+
- - ">="
|
|
22
|
+
- !ruby/object:Gem::Version
|
|
23
|
+
version: 1.2.9
|
|
24
|
+
version:
|
|
25
|
+
description: Ruby gem to cherry-pick a range of commits with similar rebase options
|
|
26
|
+
email: born2snipe@gmail.com
|
|
27
|
+
executables:
|
|
28
|
+
- cherrybase
|
|
29
|
+
extensions: []
|
|
30
|
+
|
|
31
|
+
extra_rdoc_files:
|
|
32
|
+
- LICENSE
|
|
33
|
+
- README.rdoc
|
|
34
|
+
files:
|
|
35
|
+
- .document
|
|
36
|
+
- .gitignore
|
|
37
|
+
- LICENSE
|
|
38
|
+
- README.rdoc
|
|
39
|
+
- Rakefile
|
|
40
|
+
- VERSION
|
|
41
|
+
- bin/cherrybase
|
|
42
|
+
- cherrybase.gemspec
|
|
43
|
+
- lib/args.rb
|
|
44
|
+
- lib/baser.rb
|
|
45
|
+
- lib/cmd.rb
|
|
46
|
+
- lib/executor.rb
|
|
47
|
+
- lib/file_util.rb
|
|
48
|
+
- lib/git.rb
|
|
49
|
+
- spec/args_spec.rb
|
|
50
|
+
- spec/baser_spec.rb
|
|
51
|
+
- spec/executor_spec.rb
|
|
52
|
+
- spec/file_util_spec.rb
|
|
53
|
+
- spec/git_spec.rb
|
|
54
|
+
- spec/spec.opts
|
|
55
|
+
- spec/spec_helper.rb
|
|
56
|
+
has_rdoc: true
|
|
57
|
+
homepage: http://github.com/born2snipe/cherrybase
|
|
58
|
+
licenses: []
|
|
59
|
+
|
|
60
|
+
post_install_message:
|
|
61
|
+
rdoc_options:
|
|
62
|
+
- --charset=UTF-8
|
|
63
|
+
require_paths:
|
|
64
|
+
- lib
|
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: "0"
|
|
70
|
+
version:
|
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: "0"
|
|
76
|
+
version:
|
|
77
|
+
requirements: []
|
|
78
|
+
|
|
79
|
+
rubyforge_project:
|
|
80
|
+
rubygems_version: 1.3.5
|
|
81
|
+
signing_key:
|
|
82
|
+
specification_version: 3
|
|
83
|
+
summary: Ruby gem to cherry-pick a range of commits with similar rebase options
|
|
84
|
+
test_files:
|
|
85
|
+
- spec/args_spec.rb
|
|
86
|
+
- spec/baser_spec.rb
|
|
87
|
+
- spec/executor_spec.rb
|
|
88
|
+
- spec/file_util_spec.rb
|
|
89
|
+
- spec/git_spec.rb
|
|
90
|
+
- spec/spec_helper.rb
|