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 CHANGED
@@ -1,13 +1,12 @@
1
- Git 'Er Done is a ruby tool for automating common git operations. It's similiar in concept to git flow (https://github.com/nvie/gitflow) but implements a simpler branching model.
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. I have a terrible memory for command line syntax, so I want something that reduces the number of commands I have to remember to do things right.
8
- 2. I want to understand more how to configure git process flows (much like git-flow) does, but have more configurability (via Ruby)
9
- 3. Gives an interesting test case for my Thor talk at DCRUG and Arlington RUG.
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
- Things you can do:
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://reinh.com/blog/2009/03/02/a-git-workflow-for-agile-teams.html - Target workflow
54
- http://grit.rubyforge.org/ - A Ruby wrapper around git: Might be useful for more indepth interaction with git.
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
@@ -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
- # Add everything, commit it, merge it back into the main branch.
22
- desc 'done (NAME)', 'Completes a feature (commits, squashes then merges into master). Call sync before doing this.'
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 => 'master'
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 master
45
- git :rebase => "-i master"
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(master_branch.name, current_branch.name)
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
@@ -1,7 +1,7 @@
1
1
  module Git
2
2
  module Er
3
3
  module Done
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
6
6
  end
7
7
  end
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.3.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: 2012-01-13 00:00:00.000000000Z
12
+ date: 2013-09-04 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thor
16
- requirement: &70179246615980 !ruby/object:Gem::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: *70179246615980
24
+ version_requirements: *70323278285920
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: grit
27
- requirement: &70179246615400 !ruby/object:Gem::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: *70179246615400
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.10
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.