gitloop 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in gitloop.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,61 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ gitloop (0.0.3)
5
+ methadone
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ aruba (0.4.11)
11
+ childprocess (>= 0.2.3)
12
+ cucumber (>= 1.1.1)
13
+ ffi (>= 1.0.11)
14
+ rspec (>= 2.7.0)
15
+ builder (3.0.0)
16
+ childprocess (0.3.1)
17
+ ffi (~> 1.0.6)
18
+ cucumber (1.1.4)
19
+ builder (>= 2.1.2)
20
+ diff-lcs (>= 1.1.2)
21
+ gherkin (~> 2.7.1)
22
+ json (>= 1.4.6)
23
+ term-ansicolor (>= 1.0.6)
24
+ diff-lcs (1.1.3)
25
+ ffi (1.0.11)
26
+ gherkin (2.7.7)
27
+ json (>= 1.4.6)
28
+ guard (1.0.0)
29
+ ffi (>= 0.5.0)
30
+ thor (~> 0.14.6)
31
+ guard-cucumber (0.7.5)
32
+ cucumber (>= 0.10)
33
+ guard (>= 0.8.3)
34
+ json (1.6.5)
35
+ methadone (0.5.1)
36
+ bundler
37
+ rake (0.9.2.2)
38
+ rdoc (3.12)
39
+ json (~> 1.4)
40
+ rspec (2.8.0)
41
+ rspec-core (~> 2.8.0)
42
+ rspec-expectations (~> 2.8.0)
43
+ rspec-mocks (~> 2.8.0)
44
+ rspec-core (2.8.0)
45
+ rspec-expectations (2.8.0)
46
+ diff-lcs (~> 1.1.2)
47
+ rspec-mocks (2.8.0)
48
+ ruby_gntp (0.3.4)
49
+ term-ansicolor (1.0.7)
50
+ thor (0.14.6)
51
+
52
+ PLATFORMS
53
+ ruby
54
+
55
+ DEPENDENCIES
56
+ aruba
57
+ gitloop!
58
+ guard-cucumber
59
+ rake (~> 0.9.2)
60
+ rdoc
61
+ ruby_gntp
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Alain Ravet
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.markdown ADDED
@@ -0,0 +1,49 @@
1
+ [gitloop](http://rubygems.org/gems/gitloop)
2
+ =======
3
+
4
+
5
+ <table>
6
+ <tr style="line-height:5em">
7
+ <td colspan="3" style="text-align:center;">
8
+ <code style="font-size:1.6em;padding:20px 40px;background:lightyellow">
9
+ git <b>loop</b>&nbsp;&nbsp;master~4&nbsp;&nbsp;master~2&nbsp;&nbsp;&nbsp;<b>-m</b> "let's group them"
10
+ </code>
11
+ </td>
12
+ </tr>
13
+ <tr>
14
+ <td width="42%" style="text-align:center"> <img src="https://github.com/alainravet/gitloop/raw/master/doc/BEFORE.png"/></td>
15
+ <td width="6%" style="text-align:center;font-size:4em;">&rarr;</td>
16
+ <td width="42%" style="text-align:center"> <img src="https://github.com/alainravet/gitloop/raw/master/doc/AFTER.png" /></td>
17
+ </tr>
18
+ </table>
19
+
20
+
21
+ The same results would be obtained by any of :
22
+
23
+ $ git loop 7355c79 6ffd9 -m "a logical group"
24
+ $ git loop second c4 -m "a logical group"
25
+ $ git loop c2 6ffd9 -m "a logical group"
26
+
27
+
28
+ Installation :
29
+ -------------
30
+ $ gem install gitloop
31
+
32
+ #### Report bugs to <https://github.com/alainravet/alainravet/>
33
+
34
+ --------------------------------------------------------------------------------
35
+
36
+ == Note on Patches/Pull Requests
37
+
38
+ * Fork the project.
39
+ * Make your feature addition or bug fix.
40
+ * Add tests for it. This is important so I don't break it in a
41
+ future version unintentionally.
42
+ * Commit, do not mess with rakefile, version, or history.
43
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
44
+ * Send me a pull request. Bonus points for topic branches.
45
+
46
+ == Copyright
47
+
48
+ Copyright (c) 2012 Alain Ravet. See LICENSE for details.
49
+ ·
data/bin/git-loop ADDED
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'methadone'
5
+ require File.dirname(__FILE__) + '/../lib/gitloop'
6
+
7
+ include Methadone::Main
8
+
9
+ main do |from_ref, to_ref|
10
+ all_parameters_provided = from_ref && to_ref && options[:message]
11
+ if all_parameters_provided
12
+ begin
13
+ Gitloop::Looper.make_loop_for_references_or_messages(from_ref, to_ref, options[:message])
14
+ rescue Gitloop::GitError => e
15
+ puts "*** ERROR : #{e}"
16
+ end
17
+ else
18
+ puts @banner
19
+ end
20
+ end
21
+
22
+ on("-m MESSAGE", "--message", "commit message for the loop") do |m|
23
+ options[:message] = m
24
+ end
25
+
26
+ @banner = <<-DOC
27
+ Usage: git loop <from_ref> <to_ref> -m "<message>"
28
+ where <*_ref> is either
29
+ - a commit reference (SHA, branch name, etc..) or
30
+ - a commit message part
31
+
32
+
33
+ example:
34
+
35
+ +-----------------------------------------------------------------+
36
+ | $ git loop "c2" "the third" -m "a logical group" |
37
+ | or $ git loop 7355c79 bab916a -m "a logical group" |
38
+ +-----------------------------------------------------------------+
39
+ BEFORE AFTER
40
+ -------------------------------------------------------------------
41
+ * 69fd256 c6
42
+ * 377c650 c5 five - fifth commit
43
+ * 9f31be9 c4 not the third commit
44
+ * edadbb7 c6 ----------> * 845c17e *** a logical group
45
+ * da55377 c5 five - fifth commit |\\
46
+ * 6fddd9c c4 not the third commit [ | * bab916a c3 the third commit ]
47
+ [ * bab916a c3 the third commit ] [ | * 7355c79 c2 the second commit ]
48
+ [ * 7355c79 c2 the second commit ] |/
49
+ * 91182df c1 * 91182df c1
50
+
51
+ DOC
52
+ description " combine `git merge --no--ff ..` and `git rebase ..` to regroup commits sequences in \"loops\"" +
53
+ "\n\n" +
54
+ @banner
55
+
56
+
57
+ version Gitloop::VERSION
58
+
59
+ go!
data/bin/gitloop ADDED
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'methadone'
5
+ require File.dirname(__FILE__) + '/../lib/gitloop'
6
+
7
+ include Methadone::Main
8
+
9
+ main do |from_ref, to_ref|
10
+ all_parameters_provided = from_ref && to_ref && options[:message]
11
+ if all_parameters_provided
12
+ begin
13
+ Gitloop::Looper.make_loop_for_references_or_messages(from_ref, to_ref, options[:message])
14
+ rescue Gitloop::GitError => e
15
+ puts "*** ERROR : #{e}"
16
+ end
17
+ else
18
+ puts @banner
19
+ end
20
+ end
21
+
22
+ on("-m MESSAGE", "--message", "commit message for the loop") do |m|
23
+ options[:message] = m
24
+ end
25
+
26
+ @banner = <<-DOC
27
+ Usage: git loop <from_ref> <to_ref> -m "<message>"
28
+ where <*_ref> is either
29
+ - a commit reference (SHA, branch name, etc..) or
30
+ - a commit message part
31
+
32
+
33
+ example:
34
+
35
+ +-----------------------------------------------------------------+
36
+ | $ git loop "c2" "the third" -m "a logical group" |
37
+ | or $ git loop 7355c79 bab916a -m "a logical group" |
38
+ +-----------------------------------------------------------------+
39
+ BEFORE AFTER
40
+ -------------------------------------------------------------------
41
+ * 69fd256 c6
42
+ * 377c650 c5 five - fifth commit
43
+ * 9f31be9 c4 not the third commit
44
+ * edadbb7 c6 ----------> * 845c17e *** a logical group
45
+ * da55377 c5 five - fifth commit |\\
46
+ * 6fddd9c c4 not the third commit [ | * bab916a c3 the third commit ]
47
+ [ * bab916a c3 the third commit ] [ | * 7355c79 c2 the second commit ]
48
+ [ * 7355c79 c2 the second commit ] |/
49
+ * 91182df c1 * 91182df c1
50
+
51
+ DOC
52
+ description " combine `git merge --no--ff ..` and `git rebase ..` to regroup commits sequences in \"loops\"" +
53
+ "\n\n" +
54
+ @banner
55
+
56
+
57
+ version Gitloop::VERSION
58
+
59
+ go!
data/doc/AFTER.png ADDED
Binary file
data/doc/BEFORE.png ADDED
Binary file
data/doc/TODO.txt ADDED
@@ -0,0 +1,31 @@
1
+ TESTS FOR ERROR CASES
2
+
3
+ error result
4
+ - exception
5
+ - error message in the output
6
+ ------------------------------------------------------------------------
7
+
8
+
9
+ -f -t : allow using SHA for 1/2 of the args
10
+
11
+ ABORT if
12
+ - unstaged changes
13
+ - not on a branch (current_branch.nil?)
14
+ - <from> == very 1st commit
15
+ - missing parameters
16
+ - `git` not found
17
+ - not in a git directory
18
+
19
+ after ABORT,
20
+ * reset --hard to original situation + clean if anything fails
21
+ * explain
22
+
23
+
24
+ edge case :
25
+ - empty repository
26
+
27
+ def current_SHA
28
+ `git rev-parse HEAD`
29
+ end
30
+
31
+
data/doc/dev_notes.txt ADDED
@@ -0,0 +1,6 @@
1
+
2
+ - After bootstraping the app, launch the binary skeleton
3
+
4
+ $ cd <..>/gitloop
5
+ $ bundle exec rake install
6
+ $ ./bin/gitloop --help
@@ -0,0 +1,29 @@
1
+ Feature: Errors detection and handling
2
+
3
+ As a developer
4
+ In order to improve my git history structure
5
+ I want to create a few loops via git merge and git rebase
6
+
7
+
8
+ Background:
9
+ Given the sandbox is setup
10
+ Given I cd to "sandbox"
11
+
12
+
13
+ Scenario Outline: in Ruby, create a loop from the exact SHAs
14
+
15
+
16
+ When I run `git-loop <from_ref> <to_ref> -m "fail"`
17
+
18
+ Then the repo is in its original state
19
+ And there are no unstaged nor staged changes
20
+ And the current branch is "master"
21
+
22
+ Scenarios: invalid or unknown SHAs
23
+ | from_ref | to_ref |
24
+ | 91182df | a059392 |
25
+ | 1111111 | a059392 |
26
+
27
+ Scenarios: wrong order
28
+ | from_ref | to_ref |
29
+ | a059392 | 8aab2de |
@@ -0,0 +1,58 @@
1
+ Feature: Happy path (no problems, no edge case)
2
+
3
+ As a developer
4
+ In order to improve my git history structure
5
+ I want to create a few loops via git merge and git rebase
6
+
7
+
8
+ Background:
9
+ Given the sandbox is setup
10
+ Given I cd to "sandbox"
11
+
12
+ #--------------------------------------------------------------------------------
13
+
14
+ Scenario Outline: the many ways to create the same loop
15
+
16
+
17
+ When I run `<command>`
18
+
19
+ Then the git log graph matches:
20
+ """
21
+ * <a_sha> c6
22
+ * <a_sha> c5 five - fifth commit
23
+ * <a_sha> *** hellboy was here
24
+ |\
25
+ | * 6fddd9c c4 not the third commit
26
+ | * bab916a c3 the third commit
27
+ | * 7355c79 c2 the second commit
28
+ |/
29
+ * 91182df c1
30
+ """
31
+ And the current branch is "master"
32
+
33
+ Scenarios: pure SHAs or pure messages snippets
34
+ | command |
35
+ | git-loop 7355c79 6fddd9c -m "hellboy was here" |
36
+ | git-loop master~4 master~2 -m "hellboy was here" |
37
+ | git-loop "c2" "c4" -m "hellboy was here" |
38
+ | git-loop "second commit" 6fddd9c -m "hellboy was here" |
39
+
40
+
41
+ #--------------------------------------------------------------------------------
42
+
43
+ Scenario: comment can contain quote
44
+ When I run `git-loop 7355c79 6fddd9c -m "let's do it"`
45
+
46
+ Then the current branch is "master"
47
+ And the git log graph matches:
48
+ """
49
+ * <a_sha> c6
50
+ * <a_sha> c5 five - fifth commit
51
+ * <a_sha> *** let's do it
52
+ |\
53
+ | * 6fddd9c c4 not the third commit
54
+ | * bab916a c3 the third commit
55
+ | * 7355c79 c2 the second commit
56
+ |/
57
+ * 91182df c1
58
+ """
@@ -0,0 +1,98 @@
1
+ Feature: playing in the sanbox
2
+ As a developer
3
+ In order to test `gitloop` thoroughly
4
+ I want to Install in tmp/sandbox different prepared git repositories
5
+ that cover all the possible combinations of staged/unstanged
6
+ files
7
+
8
+
9
+ Background:
10
+ * I cleared the sandbox directory
11
+ * a directory named "sandbox" should not exist
12
+
13
+
14
+ #############################
15
+ # Test the git status output :
16
+ #############################
17
+
18
+ Scenario: checking the clean sandbox
19
+
20
+ Given I unzip the clean git repo in the sandbox
21
+ * I cd to "sandbox"
22
+ Then the git status is empty
23
+
24
+
25
+ Scenario: checking the sandbox with some unstaged changes
26
+
27
+ Given I unzip the git repo with unstaged changes in the sandbox
28
+ * I cd to "sandbox"
29
+ Then the git status is:
30
+ """
31
+ M f1
32
+ ?? unstaged-new-file
33
+ """
34
+
35
+ Scenario: checking the sandbox with some staged changes
36
+
37
+ Given I unzip the git repo with staged changes in the sandbox
38
+ * I cd to "sandbox"
39
+ Then the git status is:
40
+ """
41
+ M f1
42
+ A unstaged-new-file
43
+ """
44
+
45
+
46
+
47
+ #############################
48
+ # Test the git log graph output :
49
+ #############################
50
+
51
+ Scenario Outline: the sandbox is initialized by unzipping a model git repository
52
+
53
+ Given I unzip the <repository> in the sandbox
54
+ * I cd to "sandbox"
55
+
56
+ Then the repo is in its original state
57
+ Then HEAD starts with edadbb7
58
+ * the current branch is "master"
59
+ * there are <outcome>
60
+
61
+ Examples:
62
+ | repository | outcome |
63
+ | clean git repo | no unstaged nor staged changes |
64
+
65
+
66
+
67
+ Scenario Outline: the sandbox is initialized by unzipping a model git repository
68
+
69
+ Given I unzip the <repository> in the sandbox
70
+ * I cd to "sandbox"
71
+
72
+ Then the git log graph is:
73
+ """
74
+ * a059392 c6
75
+ * e917b56 c5
76
+ * 933c4d1 c4
77
+ * 4d72f8a c3
78
+ * 8aab2de c2
79
+ * 91182df c1
80
+ """
81
+ Then the git log graph matches:
82
+ """
83
+ * a059392 c6
84
+ * <a_sha> c5
85
+ * 933c4d1 c4
86
+ * <a_sha> c3
87
+ * 8aab2de c2
88
+ * <a_sha> c1
89
+ """
90
+ Then HEAD starts with a059392
91
+ * the current branch is "master"
92
+ * there are <outcome>
93
+
94
+ Examples:
95
+ | repository | outcome |
96
+ | git repo with unstaged changes | untracked or uncommited changes|
97
+ | git repo with staged changes | untracked or uncommited changes|
98
+
@@ -0,0 +1,14 @@
1
+ Feature: My bootstrapped app kinda works
2
+ In order to get going on coding my awesome app
3
+ I want to have aruba and cucumber setup
4
+ So I don't have to do it myself
5
+
6
+ Scenario: App just runs
7
+ When I get help for "git-loop"
8
+ Then the exit status should be 0
9
+ And the banner should document that this app's arguments are:
10
+ |message|which is not optional|
11
+ And the output should contain:
12
+ """
13
+ Usage: git loop <from_ref> <to_ref> -m "<message>"
14
+ """
@@ -0,0 +1,21 @@
1
+
2
+ # Setup :
3
+ #########
4
+
5
+
6
+
7
+ # Test :
8
+ #########
9
+
10
+ Then 'the git status is empty' do
11
+ untracked_or_uncommited_changes.chomp.should be_empty
12
+ end
13
+
14
+ Then 'the git status is:' do |text|
15
+ untracked_or_uncommited_changes.chomp.should == text
16
+ end
17
+
18
+
19
+ Then 'the current branch is "$branch"' do |branch|
20
+ the_current_branch.should == "* #{branch}"
21
+ end
@@ -0,0 +1,49 @@
1
+ # -----------
2
+ # Setup :
3
+ # -----------
4
+
5
+
6
+ # -----------
7
+ # Tests :
8
+ # -----------
9
+
10
+
11
+ # Usage :
12
+ # Then HEAD starts with a059392
13
+
14
+ Then 'HEAD starts with $sha_start' do |sha_start|
15
+ in_current_dir do
16
+ `git rev-parse HEAD`.should match(/^#{sha_start}/)
17
+ end
18
+ end
19
+
20
+
21
+ # Usage :
22
+ # Then the git log graph is:
23
+ # """
24
+ # * 4d72f8a c3
25
+ # * 8aab2de c2
26
+ # * 91182df c1
27
+ # """
28
+
29
+ Then 'the git log graph is:' do |expected_output|
30
+ record_time_taken("git-log-graph-IS") do
31
+ the_git_log_graph.should == expected_output
32
+ end
33
+
34
+ end
35
+
36
+
37
+ # Usage :
38
+ # Then the git log graph matches:
39
+ # """
40
+ # * <a_sha> c3
41
+ # * 8aab2de c2
42
+ # * 91182df c1
43
+ # """
44
+
45
+ Then 'the git log graph matches:' do |raw_expected_output|
46
+ record_time_taken("git-log-graph-matches") do
47
+ the_git_log_graph.should match(make_smart_regexp(raw_expected_output))
48
+ end
49
+ end
@@ -0,0 +1,63 @@
1
+
2
+ # Setup :
3
+ #########
4
+
5
+ Given /^I cleared the sandbox directory$/ do
6
+ in_current_dir do
7
+ FileUtils.rm_rf("sandbox")
8
+ end
9
+ end
10
+
11
+ SANDBOX_PATH = {
12
+ :default => FIXTURES_PATH + '/sandbox.zip',
13
+ :staged => FIXTURES_PATH + '/sandbox-with-staged-changes.zip',
14
+ :unstaged => FIXTURES_PATH + '/sandbox-with-unstaged-changes.zip'
15
+ }
16
+
17
+ Given /^the sandbox is setup$/ do
18
+ step %Q{I cleared the sandbox directory}
19
+ step %Q{I unzip the clean git repo in the sandbox}
20
+ end
21
+
22
+
23
+ Given /^I unzip the (.*) in the sandbox$/ do |repository|
24
+ path = case repository
25
+ when 'clean git repo' then SANDBOX_PATH[:default ]
26
+ when 'git repo with staged changes' then SANDBOX_PATH[:staged ]
27
+ when 'git repo with unstaged changes' then SANDBOX_PATH[:unstaged]
28
+ else raise "NOT SUPPORTED repository : #{repository.inspect}"
29
+ end
30
+ record_time_taken("unzip") do
31
+ in_current_dir do `unzip #{path}` end # 0.5 seconds
32
+ #step %(I successfully run `unzip #{path}`) # 2.3 seconds
33
+ end
34
+ end
35
+
36
+
37
+ # Test :
38
+ #########
39
+
40
+ Then /^there are (.*)$/ do |outcome|
41
+ case outcome
42
+ when 'no unstaged nor staged changes'
43
+ untracked_or_uncommited_changes.should be_empty
44
+ when 'untracked or uncommited changes'
45
+ untracked_or_uncommited_changes.should_not be_empty
46
+ else
47
+ raise "BUG : unsupported outcome #{outcome}"
48
+ end
49
+ end
50
+
51
+
52
+ CLEAN_REPO_LOG =<<TEXT.chomp
53
+ * edadbb7 c6
54
+ * da55377 c5 five - fifth commit
55
+ * 6fddd9c c4 not the third commit
56
+ * bab916a c3 the third commit
57
+ * 7355c79 c2 the second commit
58
+ * 91182df c1
59
+ TEXT
60
+
61
+ Then /^the (?:clean repo|repo) is in its original state$/ do
62
+ the_git_log_graph.should == CLEAN_REPO_LOG
63
+ end
@@ -0,0 +1,24 @@
1
+ $timers={}
2
+
3
+
4
+ def record_time_taken(category)
5
+ category = category.to_sym
6
+ $timers[category] ||= [0,0]
7
+ start = Time.now
8
+ res = yield
9
+ time, count = $timers[category]
10
+ $timers[category] = [time + (Time.now - start), count+1]
11
+ res
12
+ end
13
+
14
+ at_exit do
15
+ puts "-"*80
16
+ keylength = $timers.keys.collect(&:length).max
17
+ puts "**** total || secs/run || runs"
18
+ puts "-"*66
19
+ $timers.keys.each do |key|
20
+ time, count = $timers[key]
21
+ #puts "**** %{key} : total = %{time} sec. || %{count} secs/run || %{loop_time} runs" % {:key => key, :time => time, :count => count, :loop_time => (time/count)}
22
+ puts "**** %#{keylength+1}s : %#7.2f sec. || %7.3f sec. || %3d runs" % [key, time, (time/count), count]
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ require 'aruba/cucumber'
2
+ require 'methadone/cucumber'
3
+
4
+ ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
5
+ LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
6
+ require LIB_DIR + '/gitloop'
7
+
8
+ FIXTURES_PATH = File.join(File.expand_path(File.dirname(__FILE__)),'fixtures')
9
+
10
+ Before do
11
+ # Using "announce" causes massive warnings on 1.9.2
12
+ @puts = true
13
+ @original_rubylib = ENV['RUBYLIB']
14
+ ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
15
+ end
16
+
17
+ After do
18
+ ENV['RUBYLIB'] = @original_rubylib
19
+ FileUtils.rm_rf File.join(File.dirname(__FILE__), '../../tmp/aruba')
20
+ end
@@ -0,0 +1,21 @@
1
+ def the_current_branch
2
+ in_current_dir do
3
+ `git branch | grep "*"`.chomp
4
+ end
5
+ end
6
+
7
+
8
+ GIT_LOG_GRAPH_COMMAND = "git log --oneline --graph"
9
+
10
+ def the_git_log_graph
11
+ step %Q{I run `#{GIT_LOG_GRAPH_COMMAND}`} # runs the actual process
12
+ output_from(GIT_LOG_GRAPH_COMMAND).chomp # only accesses the previous processes outputs
13
+ end
14
+
15
+
16
+ def untracked_or_uncommited_changes
17
+ in_current_dir do
18
+ `git status -s`
19
+ end
20
+ end
21
+
@@ -0,0 +1,23 @@
1
+
2
+ # turn the feature multiline "enhanced" text into a regexp
3
+
4
+ def make_smart_regexp(raw_expected_output)
5
+ Regexp.compile escape_xxl(raw_expected_output),
6
+ Regexp::MULTILINE
7
+ end
8
+
9
+
10
+ # turn the feature multiline "enhanced" text into a valid regexp-ready string.
11
+ # It
12
+ # - escapes the string
13
+ # - translate <a_sha> -> (\S+)
14
+ # - replace \n by \s+ because of a problem with multiline strings in Cucumber
15
+
16
+ def escape_xxl(raw_text)
17
+ Regexp.escape(raw_text).tap do |text|
18
+ text.gsub!('\n', "\\s*") # necessary (? due to Cucumber ??)
19
+ text.gsub!(/<a_sha>/, '(\S+)')
20
+ end
21
+ end
22
+
23
+
data/lib/gitloop.rb ADDED
@@ -0,0 +1,8 @@
1
+ module Gitloop
2
+
3
+ class GitError < Exception; end
4
+
5
+ end
6
+
7
+ require File.dirname(__FILE__) + "/gitloop/version"
8
+ require File.dirname(__FILE__) + "/gitloop/looper"
@@ -0,0 +1,33 @@
1
+ module Gitloop
2
+
3
+ module GitUtils
4
+
5
+ def current_branch
6
+ branch = `git branch | grep "*"`
7
+ branch.empty? ?
8
+ nil :
9
+ branch[2..-2] # remove the current branch marker : '* '
10
+ end
11
+
12
+
13
+ def invalid_commits_sequence?(first_sha, second_sha)
14
+ output = (first_sha == second_sha) ?
15
+ call_git("show #{first_sha}") :
16
+ call_git("show #{first_sha}..#{second_sha}")
17
+ error = !output.start_with?('commit')
18
+ end
19
+
20
+ def valid_commits_reference?(reference)
21
+ call_git("show #{reference}").start_with?('commit')
22
+ rescue Gitloop::GitError
23
+ false
24
+ end
25
+
26
+
27
+ def shas_for_messages_that_match(from_grep)
28
+ onelines = (`git log --oneline --grep "#{from_grep}"`).split("\n")
29
+ shas = onelines.collect { |l| l[/\w+/] }
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,59 @@
1
+ require 'open3'
2
+ require File.dirname(__FILE__) + '/git_utils'
3
+
4
+ module Gitloop
5
+
6
+ class Looper
7
+ extend GitUtils
8
+
9
+ def self.make_loop_for_references_or_messages(from, to, message=nil)
10
+ from_ref, to_ref = convert_messages_to_git_refs(from, to)
11
+ validate_commits_refs_sequence! from_ref, to_ref
12
+
13
+ build_the_loop(from_ref, message, to_ref)
14
+
15
+ loop_starts_at_very_first_commit = !valid_commits_reference?("#{from_ref}^^")
16
+ puts loop_starts_at_very_first_commit ?
17
+ call_git("log --oneline --graph --color --decorate") :
18
+ call_git("log --oneline --graph --color --decorate #{from_ref}^^..HEAD")
19
+ end
20
+
21
+ private
22
+
23
+ def self.build_the_loop(from_ref, message, to_ref)
24
+ curr_branch = current_branch()
25
+
26
+ call_git "co #{from_ref}^ --quiet"
27
+
28
+ msg = message || "loop #{from_ref}..#{to_ref}"
29
+ call_git "merge --no-ff #{to_ref} -m \"*** #{msg}\""
30
+
31
+ merge_top = call_git "rev-parse HEAD"
32
+ call_git "co --quiet #{curr_branch}"
33
+ call_git "rebase #{merge_top}"
34
+ end
35
+
36
+ def self.convert_messages_to_git_refs(from, to)
37
+ from_ref = valid_commits_reference?(from) ? from : shas_for_messages_that_match(from).last
38
+ to_ref = valid_commits_reference?(to ) ? to : shas_for_messages_that_match(to ).last
39
+ [from_ref, to_ref]
40
+ end
41
+
42
+ def self.call_git(command, error_message_prefix=nil)
43
+ output, error, status = Open3.capture3("git #{command}")
44
+ if error.empty?
45
+ output
46
+ else
47
+ raise Gitloop::GitError.new [error_message_prefix,error].compact.join("\n")
48
+ end
49
+ end
50
+
51
+ def self.validate_commits_refs_sequence!(first_sha, second_sha)
52
+ if invalid_commits_sequence?(first_sha, second_sha)
53
+ error_msg = "#{first_sha}..#{second_sha} is an invalid SHAs sequence"
54
+ raise Gitloop::GitError.new(error_msg)
55
+ end
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ module Gitloop
2
+ VERSION = "0.0.3"
3
+ end
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gitloop
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Alain Ravet
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: methadone
16
+ requirement: &70328618475780 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70328618475780
25
+ - !ruby/object:Gem::Dependency
26
+ name: rdoc
27
+ requirement: &70328618473840 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70328618473840
36
+ - !ruby/object:Gem::Dependency
37
+ name: aruba
38
+ requirement: &70328618471420 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70328618471420
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: &70328618469200 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.9.2
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70328618469200
58
+ - !ruby/object:Gem::Dependency
59
+ name: guard-cucumber
60
+ requirement: &70328618466840 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70328618466840
69
+ - !ruby/object:Gem::Dependency
70
+ name: ruby_gntp
71
+ requirement: &70328618453460 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70328618453460
80
+ description: easily merge and rebase flat git history
81
+ email:
82
+ - alainravet@gmail.com
83
+ executables:
84
+ - git-loop
85
+ - gitloop
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - bin/git-loop
90
+ - bin/gitloop
91
+ - lib/gitloop/git_utils.rb
92
+ - lib/gitloop/looper.rb
93
+ - lib/gitloop/version.rb
94
+ - lib/gitloop.rb
95
+ - doc/AFTER.png
96
+ - doc/BEFORE.png
97
+ - doc/dev_notes.txt
98
+ - doc/TODO.txt
99
+ - LICENSE
100
+ - README.markdown
101
+ - Gemfile
102
+ - Gemfile.lock
103
+ - features/errors.feature
104
+ - features/happy_path.feature
105
+ - features/sandbox.feature
106
+ - features/shell_command.feature
107
+ - features/step_definitions/git_steps.rb
108
+ - features/step_definitions/gitloop_steps.rb
109
+ - features/step_definitions/sandbox_steps.rb
110
+ - features/support/benchmarking.rb
111
+ - features/support/env.rb
112
+ - features/support/fixtures/sandbox-with-staged-changes.zip
113
+ - features/support/fixtures/sandbox-with-unstaged-changes.zip
114
+ - features/support/fixtures/sandbox.zip
115
+ - features/support/git.rb
116
+ - features/support/regexp.rb
117
+ homepage: ''
118
+ licenses: []
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ! '>='
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ segments:
130
+ - 0
131
+ hash: -412658776607306167
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ none: false
134
+ requirements:
135
+ - - ! '>='
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ segments:
139
+ - 0
140
+ hash: -412658776607306167
141
+ requirements: []
142
+ rubyforge_project: gitloop
143
+ rubygems_version: 1.8.15
144
+ signing_key:
145
+ specification_version: 3
146
+ summary: easily merge and rebase flat git history
147
+ test_files:
148
+ - features/errors.feature
149
+ - features/happy_path.feature
150
+ - features/sandbox.feature
151
+ - features/shell_command.feature
152
+ - features/step_definitions/git_steps.rb
153
+ - features/step_definitions/gitloop_steps.rb
154
+ - features/step_definitions/sandbox_steps.rb
155
+ - features/support/benchmarking.rb
156
+ - features/support/env.rb
157
+ - features/support/fixtures/sandbox-with-staged-changes.zip
158
+ - features/support/fixtures/sandbox-with-unstaged-changes.zip
159
+ - features/support/fixtures/sandbox.zip
160
+ - features/support/git.rb
161
+ - features/support/regexp.rb