git_er_done 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +64 -11
- data/features/cli.feature +54 -1
- data/features/step_definitions/cli_steps.rb +41 -0
- data/lib/git_er_done/app.rb +79 -24
- data/lib/git_er_done/version.rb +1 -1
- metadata +7 -7
data/README.md
CHANGED
@@ -1,13 +1,12 @@
|
|
1
|
-
Git 'Er Done is a ruby tool for automating common git operations. It's
|
1
|
+
Git 'Er Done is a ruby tool for automating common git operations. It's similar in concept to git flow (https://github.com/nvie/gitflow) but is designed for small teams. It's branching model is quite a bit simpler but still makes it easy to keep an organized Git repository.
|
2
2
|
|
3
3
|
## Reasons for this project
|
4
4
|
|
5
5
|
This project exists for a few reasons:
|
6
6
|
|
7
|
-
1.
|
8
|
-
|
9
|
-
|
10
|
-
4. Make doing the 'right' thing easy (i.e. feature branches should be simple. Smaller projects)
|
7
|
+
1. Encourage good habits: Practices like rebasing, feature branches and squashing commits make code repositories easy to follow.
|
8
|
+
1. Make 'Right way' easy: Doing proper git workflows require a lot of commands. We should script them to make them easy.
|
9
|
+
1. Exploration: I want to understand more how to configure git process flows (much like git-flow) does, but have more configurability (via Ruby)
|
11
10
|
|
12
11
|
## Installation
|
13
12
|
|
@@ -17,9 +16,63 @@ gem install git_er_done
|
|
17
16
|
|
18
17
|
This makes the `gd` command available on your system.
|
19
18
|
|
19
|
+
|
20
|
+
## What you get
|
21
|
+
|
22
|
+
### 1. Starting Feature Branches
|
23
|
+
|
24
|
+
When you work locally, it's helpful to work in a feature branch. To create a feature branch, you would normally do something like:
|
25
|
+
|
26
|
+
```
|
27
|
+
git checkout -b features/new_widget
|
28
|
+
```
|
29
|
+
|
30
|
+
With GitErDone, you can type the following to get the same thing:
|
31
|
+
|
32
|
+
```
|
33
|
+
$ gd feature new_widget
|
34
|
+
````
|
35
|
+
|
36
|
+
Really, the only benefit from this is prefixing branches with features/, which provides some context to your branch names. (Later, we might add bugs/ and/or 'remote-features')
|
37
|
+
|
38
|
+
### 2. Finishing a feature
|
39
|
+
|
40
|
+
After you are done with your feature, you might want to do a few things:
|
41
|
+
|
42
|
+
1. Squash your feature's commits into a single commit
|
43
|
+
1. Checkout master
|
44
|
+
1. Merge your changes
|
45
|
+
1. Delete the branch
|
46
|
+
|
47
|
+
That's a lot of commands to remember and type. Instead, you can do:
|
48
|
+
|
49
|
+
```
|
50
|
+
gd done
|
51
|
+
```
|
52
|
+
|
53
|
+
This will trigger an [interactive rebase](http://book.git-scm.com/4_interactive_rebasing.html) including all changes made since master. You can choose to squash them (or not). Then it will merge your changes with master, and delete your branch.
|
54
|
+
|
55
|
+
Pretty simple.
|
56
|
+
|
57
|
+
### 3. Keeping in Sync with the master using rebase
|
58
|
+
|
59
|
+
Unless you are working by yourself, its likely you may start a feature and before you can merge and push your changes, somebody is going to push changes. It's going to be a lot easier to reconcile your changes sooner than later. With GitErDone, you can do the following when working in your feature branch.
|
60
|
+
|
61
|
+
```
|
62
|
+
(features/new_widget)$ gd sync
|
63
|
+
```
|
64
|
+
|
65
|
+
This will do the following:
|
66
|
+
|
67
|
+
1. Checkout master and then pull
|
68
|
+
1. Checkout your branch
|
69
|
+
1. Rebase your branch from master. See [rebasing](http://book.git-scm.com/4_rebasing.html) for more info.
|
70
|
+
|
71
|
+
Syncing frequently will help avoid nasty merges later.
|
72
|
+
|
20
73
|
## Syntax
|
21
74
|
|
22
|
-
|
75
|
+
Command reference:
|
23
76
|
|
24
77
|
```
|
25
78
|
gd - Lists all available commands.
|
@@ -29,6 +82,7 @@ gd done new_widget - Completes a feature branch with the name 'new_widget' (Com
|
|
29
82
|
gd done - Completes the current feature branch you are on.
|
30
83
|
gd squash - Condenses multiple commits for the current branch into a single commit.
|
31
84
|
gd sync - Brings your branch up to date with the latest version (Use before finishing a feature)
|
85
|
+
gd inception - Determines the original branch(es) your current commit came from.
|
32
86
|
```
|
33
87
|
|
34
88
|
## Goals
|
@@ -36,7 +90,7 @@ gd sync - Brings your branch up to date with the latest version (Use before fini
|
|
36
90
|
Here's what I want to be able to support
|
37
91
|
|
38
92
|
* Creating and closing feature branches should be simple.
|
39
|
-
* Small projects that can work from and merge to master shouldn't need a 'develop' branch.
|
93
|
+
* Small projects that can work from and merge to master shouldn't need a 'develop' branch. (i.e. git-flow)
|
40
94
|
* Should automatically squashing commits from feature branches into a single commit via rebase
|
41
95
|
* Support git-flow's Feature, hotfix, release branches to/from develop if necessary.
|
42
96
|
* Provide a nice discoverable CLI for doing this sort of thing.
|
@@ -45,11 +99,10 @@ Here's what I want to be able to support
|
|
45
99
|
|
46
100
|
* Improve error messages for incorrectly supplied parameters. (i.e. gd feature)
|
47
101
|
* If you gd sync with unstaged changes, it should not switch branches. (It does, which is probably wrong)
|
48
|
-
* Figure out how to unit test this.
|
49
102
|
* Better error checking (merges failing may cause problems)
|
50
103
|
|
51
104
|
## References
|
52
105
|
|
53
|
-
http://
|
54
|
-
http://
|
55
|
-
http://stackoverflow.com/questions/5294069/best-way-to-create-an-executable-for-a-gem-using-rake - Minimum work to make a CLI bin file.
|
106
|
+
* Interactive Rebasing (i.e. Squashing Commits) - http://book.git-scm.com/4_interactive_rebasing.html
|
107
|
+
* Target workflow - http://reinh.com/blog/2009/03/02/a-git-workflow-for-agile-teams.html -
|
108
|
+
* http://stackoverflow.com/questions/5294069/best-way-to-create-an-executable-for-a-gem-using-rake - Minimum work to make a CLI bin file.
|
data/features/cli.feature
CHANGED
@@ -27,4 +27,57 @@ Feature: CLI
|
|
27
27
|
And the output should contain "Switched to branch 'features/new_widget"
|
28
28
|
And the output should contain "Current branch features/new_widget is up to date"
|
29
29
|
|
30
|
-
|
30
|
+
Scenario: Inception of a branch
|
31
|
+
Given I am working on a git project
|
32
|
+
And I run `git checkout -b staging`
|
33
|
+
And I commit a new file
|
34
|
+
When I run `gd feature new_widget`
|
35
|
+
And I commit another file
|
36
|
+
And I run `gd inception`
|
37
|
+
Then the output should contain:
|
38
|
+
"""
|
39
|
+
Current branch 'features/new_widget' was forked from 'staging'
|
40
|
+
"""
|
41
|
+
|
42
|
+
Scenario: Ambiguous inception of a branch
|
43
|
+
Given I am working on a git project
|
44
|
+
And I run `git checkout -b staging`
|
45
|
+
When I run `gd feature new_widget`
|
46
|
+
And I commit a new file
|
47
|
+
And I run `gd inception`
|
48
|
+
Then the output should contain:
|
49
|
+
"""
|
50
|
+
Current branch 'features/new_widget' matches the following branches: 'master, staging'
|
51
|
+
"""
|
52
|
+
|
53
|
+
Scenario: Completing work branched from a non-master branch
|
54
|
+
Given I am working on a git project
|
55
|
+
And I have been working in the "staging" branch
|
56
|
+
When I run `gd feature new_widget`
|
57
|
+
And I commit a new file
|
58
|
+
And I run `gd done`
|
59
|
+
Then the output should contain:
|
60
|
+
"""
|
61
|
+
Switched to branch 'staging'
|
62
|
+
"""
|
63
|
+
|
64
|
+
Scenario: Completing work that could have been from multiple branches
|
65
|
+
Given I am working on a git project
|
66
|
+
And I run `git checkout -b staging`
|
67
|
+
When I run `gd feature new_widget`
|
68
|
+
And I commit a new file
|
69
|
+
And I run `gd done` interactively
|
70
|
+
And I type "1"
|
71
|
+
Then the output should contain:
|
72
|
+
"""
|
73
|
+
Switched to branch 'master'
|
74
|
+
"""
|
75
|
+
|
76
|
+
Scenario: Selecting an invalid branch number.
|
77
|
+
Given I am working on a git project
|
78
|
+
And I run `git checkout -b staging`
|
79
|
+
When I run `gd feature new_widget`
|
80
|
+
And I commit a new file
|
81
|
+
And I run `gd done` interactively
|
82
|
+
And I type "3"
|
83
|
+
Then the program should fail using Thor's erroneous exit codes
|
@@ -12,4 +12,45 @@ Given /^I am working on the "(.*)" branch$/ do |branch|
|
|
12
12
|
end
|
13
13
|
When /^should display the current version$/ do
|
14
14
|
assert_partial_output(Git::Er::Done::VERSION, all_output)
|
15
|
+
end
|
16
|
+
|
17
|
+
When /^I commit a new file$/ do
|
18
|
+
write_file('first.md', "A commit")
|
19
|
+
run_simple "git add ."
|
20
|
+
run_simple "git commit -m 'Add a file'"
|
21
|
+
end
|
22
|
+
|
23
|
+
When /^I commit another file$/ do
|
24
|
+
write_file('second.md', "A commit")
|
25
|
+
run_simple "git add ."
|
26
|
+
run_simple "git commit -m 'Add a file'"
|
27
|
+
end
|
28
|
+
|
29
|
+
When /^I make several commits$/ do
|
30
|
+
write_file('first.md', "A commit")
|
31
|
+
run_simple "git add ."
|
32
|
+
run_simple "git commit -m 'Add a file'"
|
33
|
+
|
34
|
+
write_file('second.md', "A commit")
|
35
|
+
run_simple "git add ."
|
36
|
+
run_simple "git commit -m 'Add a file'"
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
When /^I have been working in the "([^"]*)" branch$/ do |branch_name|
|
41
|
+
steps %Q{
|
42
|
+
And I run `git checkout -b #{branch_name}`
|
43
|
+
}
|
44
|
+
write_file("#{branch_name}.md", "First commit for #{branch_name}")
|
45
|
+
run_simple "git add ."
|
46
|
+
run_simple "git commit -m 'First commit for #{branch_name}'"
|
47
|
+
end
|
48
|
+
|
49
|
+
Then /^the program should fail using Thor's erroneous exit codes$/ do
|
50
|
+
# This really should be 1, but thor doesn't seem to respect error statuses
|
51
|
+
# See http://stackoverflow.com/questions/17241932/ruby-thor-exit-status-in-case-of-an-error
|
52
|
+
steps %Q{
|
53
|
+
Then the exit status should be 0
|
54
|
+
And the output should contain "Error!"
|
55
|
+
}
|
15
56
|
end
|
data/lib/git_er_done/app.rb
CHANGED
@@ -6,51 +6,64 @@ module Git
|
|
6
6
|
module Er
|
7
7
|
module Done
|
8
8
|
class App < Thor
|
9
|
-
|
9
|
+
|
10
10
|
include Thor::Actions
|
11
11
|
include Git::Er::Done::Actions
|
12
|
-
|
12
|
+
|
13
13
|
FEATURES_PATH = "features/"
|
14
|
-
|
14
|
+
|
15
15
|
desc 'feature [NAME]', 'Start a new feature using a branch.'
|
16
16
|
def feature(name)
|
17
17
|
puts "Creating a new feature called #{feature_branch(name)}."
|
18
18
|
git :checkout => "-b #{feature_branch(name)}"
|
19
19
|
end
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
|
21
|
+
desc 'inception', 'Find the branch(es) that the current branch was originally created from.'
|
22
|
+
def inception
|
23
|
+
# Find the first commit that matches one of the heads.
|
24
|
+
matching_branches = inception_branches()
|
25
|
+
|
26
|
+
if matching_branches.size > 1
|
27
|
+
puts "Current branch '#{current_branch.name}' matches the following branches: '#{matching_branches.collect {|b| b.name}.join(", ")}'"
|
28
|
+
else
|
29
|
+
puts "Current branch '#{current_branch.name}' was forked from '#{matching_branches.first.name}'"
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'done (NAME)', 'Completes a feature (commits, squashes then merges into the inception branch).
|
35
|
+
Recommend calling sync before doing this.'
|
23
36
|
def done(name=nil)
|
24
37
|
unless name
|
25
|
-
name = current_feature
|
38
|
+
name = current_feature
|
26
39
|
end
|
27
40
|
puts "Completing a feature called #{feature_branch(name)}"
|
28
41
|
git :add=>"."
|
29
42
|
git :commit
|
30
43
|
squash
|
31
44
|
# Should we also sync with master first?
|
32
|
-
git :checkout =>
|
45
|
+
git :checkout => inception_branch_name
|
33
46
|
git :merge => feature_branch(name)
|
34
|
-
git :branch => "-d #{feature_branch(name)}"
|
47
|
+
git :branch => "-d #{feature_branch(name)}"
|
35
48
|
end
|
36
|
-
|
37
|
-
desc 'squash', 'Squash all commits from the current branch into a single commit.'
|
49
|
+
|
50
|
+
desc 'squash', 'Squash all commits from the current branch into a single commit (since inception).'
|
38
51
|
def squash
|
39
52
|
new_commits = commits_in_feature_branch.size
|
40
53
|
if new_commits < 2
|
41
54
|
puts "Only '#{new_commits}' new commits in this branch, so no squashing necessary."
|
42
55
|
return
|
43
56
|
end
|
44
|
-
# Squash all changes since we branched away from
|
45
|
-
git :rebase => "-i
|
57
|
+
# Squash all changes since we branched away from inception branch
|
58
|
+
git :rebase => "-i #{inception_branch_name}"
|
46
59
|
end
|
47
|
-
|
60
|
+
|
48
61
|
desc 'info', "Report information about the current feature branch you are in."
|
49
62
|
def info
|
50
63
|
commits = commits_in_feature_branch
|
51
64
|
puts "There are #{commits.size} new commits for #{current_branch_name}"
|
52
65
|
end
|
53
|
-
|
66
|
+
|
54
67
|
desc 'sync', 'Update your branch with the latest from master.'
|
55
68
|
def sync
|
56
69
|
return_to_branch = current_branch_name
|
@@ -65,13 +78,55 @@ module Git
|
|
65
78
|
def version
|
66
79
|
puts "Git-Er-Done #{Git::Er::Done::VERSION}"
|
67
80
|
end
|
81
|
+
|
68
82
|
private
|
69
|
-
|
83
|
+
|
84
|
+
def inception_branch_name
|
85
|
+
return @inception_branch.name if @inception_branch
|
86
|
+
branches = inception_branches
|
87
|
+
@inception_branch = if branches.size > 1
|
88
|
+
say 'There is more than one possible inception branch for your current feature.', :green
|
89
|
+
branches.each_with_index do |branch, i|
|
90
|
+
say "#{i + 1}. #{branch.name}", :yellow
|
91
|
+
end
|
92
|
+
index = ask "Which would you like merge it into?:", :green
|
93
|
+
unless index.to_i.between?(1, branches.size)
|
94
|
+
say "Error! Invalid branch selected.", :red
|
95
|
+
exit
|
96
|
+
end
|
97
|
+
branches[index.to_i - 1]
|
98
|
+
else
|
99
|
+
branches.first
|
100
|
+
end
|
101
|
+
@inception_branch.name
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns a list of branches that the current commit could have been originated from.
|
105
|
+
# @return [Array<Grit::Head>]
|
106
|
+
def inception_branches
|
107
|
+
commit = current_branch.commit
|
108
|
+
|
109
|
+
# Look through the parent history of this branch
|
110
|
+
while true
|
111
|
+
matching_branches = long_running_branches.select { |branch| commit.id == branch.commit.id }
|
112
|
+
return matching_branches unless matching_branches.empty?
|
113
|
+
commit = commit.parents.first
|
114
|
+
end
|
115
|
+
[]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns all branches that are 'long lived', i.e. not a feature.
|
119
|
+
#
|
120
|
+
# @return [Array<Grit::Head>]
|
121
|
+
def long_running_branches
|
122
|
+
repo.heads.select { |head| !head.name.include?('features') }
|
123
|
+
end
|
124
|
+
|
70
125
|
# Returns a list of all commits for the current branch since it was forked from master.
|
71
126
|
def commits_in_feature_branch
|
72
|
-
repo.commits_between(
|
127
|
+
repo.commits_between(inception_branch_name, current_branch.name)
|
73
128
|
end
|
74
|
-
|
129
|
+
|
75
130
|
# Returns the name of the feature for the current branch
|
76
131
|
# @return [String] Name of feature (not include features/)
|
77
132
|
def current_feature
|
@@ -81,27 +136,27 @@ module Git
|
|
81
136
|
end
|
82
137
|
return ""
|
83
138
|
end
|
84
|
-
|
139
|
+
|
85
140
|
def current_branch
|
86
141
|
repo.head
|
87
142
|
end
|
88
|
-
|
143
|
+
|
89
144
|
def current_branch_name
|
90
145
|
current_branch.name
|
91
146
|
end
|
92
|
-
|
147
|
+
|
93
148
|
def repo
|
94
149
|
repo ||= Grit::Repo.new('.')
|
95
150
|
end
|
96
|
-
|
151
|
+
|
97
152
|
def master_branch
|
98
153
|
repo.heads.each do |head|
|
99
154
|
return head if head.name == "master"
|
100
155
|
end
|
101
156
|
raise "No branch named 'master' found."
|
102
157
|
end
|
103
|
-
|
104
|
-
def feature_branch(name)
|
158
|
+
|
159
|
+
def feature_branch(name)
|
105
160
|
"#{FEATURES_PATH}#{name}"
|
106
161
|
end
|
107
162
|
end
|
data/lib/git_er_done/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git_er_done
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-09-04 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: thor
|
16
|
-
requirement: &
|
16
|
+
requirement: &70323278285920 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 0.14.6
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70323278285920
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: grit
|
27
|
-
requirement: &
|
27
|
+
requirement: &70323278285460 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: '2.4'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70323278285460
|
36
36
|
description: A gem to assist with git workflow, similar to git-flow.
|
37
37
|
email:
|
38
38
|
- peakpg@gmail.com
|
@@ -75,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
75
|
version: '0'
|
76
76
|
requirements: []
|
77
77
|
rubyforge_project: git_er_done
|
78
|
-
rubygems_version: 1.8.
|
78
|
+
rubygems_version: 1.8.15
|
79
79
|
signing_key:
|
80
80
|
specification_version: 3
|
81
81
|
summary: A gem to assist with git workflow.
|