v2gpti 1.1.9 → 1.2.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.
- checksums.yaml +4 -4
- data/bin/git-deliver +1 -1
- data/bin/git-finish +1 -1
- data/bin/git-newbug +1 -1
- data/bin/git-newfeature +1 -1
- data/bin/git-qa +1 -1
- data/bin/git-release +1 -1
- data/bin/git-report +1 -1
- data/bin/git-start +1 -1
- data/bin/git-uat +1 -1
- data/config_template +16 -0
- data/lib/git-pivotal-tracker-integration/command/base.rb +235 -322
- data/lib/git-pivotal-tracker-integration/command/configuration.rb +183 -109
- data/lib/git-pivotal-tracker-integration/command/deliver.rb +145 -200
- data/lib/git-pivotal-tracker-integration/command/finish.rb +70 -63
- data/lib/git-pivotal-tracker-integration/command/newbug.rb +36 -39
- data/lib/git-pivotal-tracker-integration/command/newfeature.rb +43 -42
- data/lib/git-pivotal-tracker-integration/command/release.rb +171 -203
- data/lib/git-pivotal-tracker-integration/command/report.rb +33 -36
- data/lib/git-pivotal-tracker-integration/command/start.rb +74 -78
- data/lib/git-pivotal-tracker-integration/util/git.rb +202 -204
- data/lib/git-pivotal-tracker-integration/util/shell.rb +19 -16
- data/lib/git-pivotal-tracker-integration/util/story.rb +155 -177
- data/lib/git-pivotal-tracker-integration/version-update/gradle.rb +44 -40
- data/lib/git-pivotal-tracker-integration.rb +44 -0
- data/spec/git-pivotal-tracker-integration/command/configuration_spec.rb +1 -2
- data/spec/git-pivotal-tracker-integration/command/finish_spec.rb +1 -1
- data/spec/git-pivotal-tracker-integration/command/release_spec.rb +1 -1
- data/spec/git-pivotal-tracker-integration/command/start_spec.rb +1 -1
- data/spec/git-pivotal-tracker-integration/util/story_spec.rb +21 -32
- data/tracker_api/lib/tracker_api/client.rb +241 -0
- data/tracker_api/lib/tracker_api/endpoints/activity.rb +38 -0
- data/tracker_api/lib/tracker_api/endpoints/comments.rb +27 -0
- data/tracker_api/lib/tracker_api/endpoints/epic.rb +17 -0
- data/tracker_api/lib/tracker_api/endpoints/epics.rb +20 -0
- data/tracker_api/lib/tracker_api/endpoints/file_attachment.rb +18 -0
- data/tracker_api/lib/tracker_api/endpoints/iterations.rb +20 -0
- data/tracker_api/lib/tracker_api/endpoints/me.rb +17 -0
- data/tracker_api/lib/tracker_api/endpoints/memberships.rb +20 -0
- data/tracker_api/lib/tracker_api/endpoints/notifications.rb +20 -0
- data/tracker_api/lib/tracker_api/endpoints/project.rb +17 -0
- data/tracker_api/lib/tracker_api/endpoints/projects.rb +18 -0
- data/tracker_api/lib/tracker_api/endpoints/stories.rb +20 -0
- data/tracker_api/lib/tracker_api/endpoints/story.rb +37 -0
- data/tracker_api/lib/tracker_api/endpoints/tasks.rb +20 -0
- data/tracker_api/lib/tracker_api/error.rb +18 -0
- data/tracker_api/lib/tracker_api/logger.rb +31 -0
- data/tracker_api/lib/tracker_api/resources/account.rb +18 -0
- data/tracker_api/lib/tracker_api/resources/activity.rb +24 -0
- data/tracker_api/lib/tracker_api/resources/base.rb +71 -0
- data/tracker_api/lib/tracker_api/resources/change.rb +15 -0
- data/tracker_api/lib/tracker_api/resources/comment.rb +20 -0
- data/tracker_api/lib/tracker_api/resources/epic.rb +17 -0
- data/tracker_api/lib/tracker_api/resources/file_attachment.rb +23 -0
- data/tracker_api/lib/tracker_api/resources/iteration.rb +24 -0
- data/tracker_api/lib/tracker_api/resources/label.rb +14 -0
- data/tracker_api/lib/tracker_api/resources/me.rb +21 -0
- data/tracker_api/lib/tracker_api/resources/membership_summary.rb +15 -0
- data/tracker_api/lib/tracker_api/resources/notification.rb +26 -0
- data/tracker_api/lib/tracker_api/resources/person.rb +14 -0
- data/tracker_api/lib/tracker_api/resources/primary_resource.rb +13 -0
- data/tracker_api/lib/tracker_api/resources/project.rb +131 -0
- data/tracker_api/lib/tracker_api/resources/project_membership.rb +16 -0
- data/tracker_api/lib/tracker_api/resources/story.rb +102 -0
- data/tracker_api/lib/tracker_api/resources/task.rb +16 -0
- data/tracker_api/lib/tracker_api/resources/time_zone.rb +13 -0
- data/tracker_api/lib/tracker_api/version.rb +3 -0
- data/tracker_api/lib/tracker_api.rb +60 -0
- metadata +202 -53
- data/lib/git-pivotal-tracker-integration/command/command.rb +0 -20
- data/lib/git-pivotal-tracker-integration/util/util.rb +0 -20
- data/lib/git-pivotal-tracker-integration/version-update/version_update.rb +0 -20
- data/lib/git_pivotal_tracker_integration.rb +0 -18
@@ -13,99 +13,95 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
require 'git-pivotal-tracker-integration/util/git'
|
19
|
-
require 'git-pivotal-tracker-integration/util/story'
|
20
|
-
require 'pivotal-tracker'
|
21
|
-
|
22
|
-
# The class that encapsulates starting a Pivotal Tracker Story
|
23
|
-
class GitPivotalTrackerIntegration::Command::Start < GitPivotalTrackerIntegration::Command::Base
|
24
|
-
|
25
|
-
# Starts a Pivotal Tracker story by doing the following steps:
|
26
|
-
# * Create a branch
|
27
|
-
# * Add default commit hook
|
28
|
-
# * Start the story on Pivotal Tracker
|
29
|
-
#
|
30
|
-
# @param [String, nil] filter a filter for selecting the story to start. This
|
31
|
-
# filter can be either:
|
32
|
-
# * a story id
|
33
|
-
# * a story type (feature, bug, chore)
|
34
|
-
# * +nil+
|
35
|
-
# @return [void]
|
36
|
-
def run(args)
|
37
|
-
my_projects = PivotalTracker::Project.all
|
38
|
-
filter = args[0]
|
39
|
-
$LOG.debug("#{self.class} in project:#{@project.name} pwd:#{pwd} branch:#{GitPivotalTrackerIntegration::Util::Git.branch_name} args:#{filter}")
|
40
|
-
self.check_branch
|
41
|
-
story = nil
|
42
|
-
if (!args.nil? && args.any?{|arg| arg.include?("-n")})
|
43
|
-
story = self.create_story(args)
|
44
|
-
else
|
45
|
-
story = GitPivotalTrackerIntegration::Util::Story.select_story @project, filter
|
46
|
-
end
|
47
|
-
if story.nil?
|
48
|
-
abort "There are no available stories."
|
49
|
-
end
|
50
|
-
if story.story_type == "feature" && story.estimate < 0
|
51
|
-
estimate_story(story)
|
52
|
-
end
|
53
|
-
$LOG.debug("story:#{story.name}")
|
54
|
-
GitPivotalTrackerIntegration::Util::Story.pretty_print story
|
16
|
+
module GitPivotalTrackerIntegration
|
17
|
+
module Command
|
55
18
|
|
56
|
-
|
57
|
-
|
58
|
-
@configuration.story = story
|
59
|
-
GitPivotalTrackerIntegration::Util::Git.add_hook 'prepare-commit-msg', File.join(File.dirname(__FILE__), !OS.windows? ? 'prepare-commit-msg.sh' : 'prepare-commit-msg-win.sh' )
|
19
|
+
# The class that encapsulates starting a Pivotal Tracker Story
|
20
|
+
class Start < Base
|
60
21
|
|
61
|
-
|
62
|
-
|
22
|
+
# Starts a Pivotal Tracker story by doing the following steps:
|
23
|
+
# * Create a branch
|
24
|
+
# * Add default commit hook
|
25
|
+
# * Start the story on Pivotal Tracker
|
26
|
+
#
|
27
|
+
# @param [String, nil] filter a filter for selecting the story to start. This
|
28
|
+
# filter can be either:
|
29
|
+
# * a story id
|
30
|
+
# * a story type (feature, bug, chore)
|
31
|
+
# * +nil+
|
32
|
+
# @return [void]
|
33
|
+
def run(args)
|
34
|
+
filter = args[0]
|
35
|
+
$LOG.debug("#{self.class} in project:#{@project.name} pwd:#{pwd} branch:#{Util::Git.branch_name} args:#{filter}")
|
36
|
+
self.check_branch
|
37
|
+
story = nil
|
38
|
+
if (!args.nil? && args.any?{|arg| arg.include?("-n")})
|
39
|
+
story = self.create_story(args)
|
40
|
+
else
|
41
|
+
story = Util::Story.select_story @project, filter
|
42
|
+
end
|
63
43
|
|
64
|
-
|
44
|
+
abort "There are no available stories." if story.nil?
|
65
45
|
|
66
|
-
|
67
|
-
|
68
|
-
|
46
|
+
if story.story_type == "feature" && story.estimate.to_i <= 0
|
47
|
+
story.estimate = estimate_story
|
48
|
+
story.save
|
49
|
+
end
|
69
50
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
$LOG.debug(GitPivotalTrackerIntegration::Util::Shell.exec "git checkout --quiet #{suggested_branch}")
|
77
|
-
end
|
51
|
+
$LOG.debug("story:#{story.name}")
|
52
|
+
Util::Story.pretty_print story
|
53
|
+
|
54
|
+
Util::Git.create_branch feature_branch(story)
|
55
|
+
@configuration.story = story
|
56
|
+
Util::Git.add_hook 'prepare-commit-msg', File.join(File.dirname(__FILE__), !OS.windows? ? 'prepare-commit-msg.sh' : 'prepare-commit-msg-win.sh' )
|
78
57
|
|
58
|
+
start_on_tracker story
|
79
59
|
end
|
80
60
|
|
81
|
-
|
61
|
+
def check_branch
|
62
|
+
current_branch = Util::Git.branch_name
|
63
|
+
# suggested_branch = (Util::Shell.exec "git config --get git-pivotal-tracker-integration.feature-root 2>/dev/null", false).chomp
|
64
|
+
suggested_branch = 'develop'
|
65
|
+
|
66
|
+
if !suggested_branch.nil? && suggested_branch.length !=0 && current_branch != suggested_branch
|
67
|
+
$LOG.warn("Currently checked out branch is '#{current_branch}'.")
|
68
|
+
should_change_branch = ask("Your currently checked out branch is '#{current_branch}'. Do you want to checkout '#{suggested_branch}' before starting?(Y/n)")
|
69
|
+
if should_change_branch != "n"
|
70
|
+
$LOG.debug("Checking out branch '#{suggested_branch}'")
|
71
|
+
print "Checking out branch '#{suggested_branch}'...\n\n"
|
72
|
+
$LOG.debug(Util::Shell.exec "git checkout --quiet #{suggested_branch}")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
82
76
|
|
83
|
-
|
77
|
+
private
|
84
78
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
79
|
+
def feature_branch(story)
|
80
|
+
prefix = "#{story.id}-"
|
81
|
+
story_name = "#{story.name.gsub(/[^0-9a-z\\s]/i, '_')}"
|
82
|
+
if(story_name.length > 30)
|
89
83
|
suggested_suffix = story_name[0..27]
|
90
84
|
suggested_suffix << "__"
|
91
|
-
|
85
|
+
else
|
92
86
|
suggested_suffix = story_name
|
87
|
+
end
|
88
|
+
branch_name = ask("Enter branch name (#{story.id}-<#{suggested_suffix}>): ")
|
89
|
+
branch_name = branch_name.empty? ? "#{prefix}#{suggested_suffix}" : "#{prefix}#{branch_name}"
|
90
|
+
branch_name.gsub(/[^0-9a-z\\s\-]/i, '_')
|
93
91
|
end
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
92
|
+
|
93
|
+
def start_on_tracker(story)
|
94
|
+
print 'Starting story on Pivotal Tracker... '
|
95
|
+
story.attributes = {
|
96
|
+
:current_state => 'started',
|
97
|
+
:owned_by => Util::Git.get_config('user.name')
|
98
|
+
}
|
99
|
+
story.save
|
100
|
+
puts 'OK'
|
98
101
|
end
|
99
|
-
branch_name.gsub(/[^0-9a-z\\s\-]/i, '_')
|
100
|
-
end
|
101
102
|
|
102
|
-
|
103
|
-
print 'Starting story on Pivotal Tracker... '
|
104
|
-
story.update(
|
105
|
-
:current_state => 'started',
|
106
|
-
:owned_by => GitPivotalTrackerIntegration::Util::Git.get_config('user.name')
|
107
|
-
)
|
108
|
-
puts 'OK'
|
109
|
-
end
|
103
|
+
end
|
110
104
|
|
105
|
+
end
|
111
106
|
end
|
107
|
+
|
@@ -13,232 +13,230 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# Utilities for dealing with Git
|
20
|
-
class
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
File.
|
38
|
-
|
39
|
-
|
16
|
+
module GitPivotalTrackerIntegration
|
17
|
+
module Util
|
18
|
+
|
19
|
+
# Utilities for dealing with Git
|
20
|
+
class Git
|
21
|
+
|
22
|
+
KEY_REMOTE = 'remote'.freeze
|
23
|
+
KEY_ROOT_BRANCH = 'root-branch'.freeze
|
24
|
+
KEY_ROOT_REMOTE = 'root-remote'.freeze
|
25
|
+
RELEASE_BRANCH_NAME = 'pivotal-tracker-release'.freeze
|
26
|
+
|
27
|
+
# Adds a Git hook to the current repository
|
28
|
+
#
|
29
|
+
# @param [String] name the name of the hook to add
|
30
|
+
# @param [String] source the file to use as the source for the created hook
|
31
|
+
# @param [Boolean] overwrite whether to overwrite the hook if it already exists
|
32
|
+
# @return [void]
|
33
|
+
def self.add_hook(name, source, overwrite = false)
|
34
|
+
hooks_directory = File.join repository_root, '.git', 'hooks'
|
35
|
+
hook = File.join hooks_directory, name
|
36
|
+
|
37
|
+
if overwrite || !File.exist?(hook)
|
38
|
+
print "Creating Git hook #{name}... "
|
39
|
+
|
40
|
+
FileUtils.mkdir_p hooks_directory
|
41
|
+
File.open(source, 'r') do |input|
|
42
|
+
File.open(hook, 'w') do |output|
|
43
|
+
output.write(input.read)
|
44
|
+
output.chmod(0755)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
puts 'OK'
|
40
49
|
end
|
41
50
|
end
|
42
51
|
|
43
|
-
|
44
|
-
|
45
|
-
|
52
|
+
# Returns the name of the currently checked out branch
|
53
|
+
#
|
54
|
+
# @return [String] the name of the currently checked out branch
|
55
|
+
def self.branch_name
|
56
|
+
Util::Shell.exec('git branch').scan(/\* (.*)/)[0][0]
|
57
|
+
end
|
46
58
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
59
|
+
# Creates a branch with a given +name+. First pulls the current branch to
|
60
|
+
# ensure that it is up to date and then creates and checks out the new
|
61
|
+
# branch. If specified, sets branch-specific properties that are passed in.
|
62
|
+
#
|
63
|
+
# @param [String] name the name of the branch to create
|
64
|
+
# @param [Boolean] print_messages whether to print messages
|
65
|
+
# @return [void]
|
66
|
+
def self.create_branch(name, print_messages = true)
|
67
|
+
root_branch = branch_name
|
68
|
+
root_remote = get_config KEY_REMOTE, :branch
|
69
|
+
|
70
|
+
print "Pulling #{root_branch}... " if print_messages
|
71
|
+
Util::Shell.exec 'git pull --quiet --ff-only'
|
72
|
+
puts 'OK' if print_messages
|
73
|
+
|
74
|
+
print "Creating and checking out #{name}... " if print_messages
|
75
|
+
|
76
|
+
Util::Shell.exec "git checkout --quiet -b #{name}"
|
77
|
+
set_config KEY_ROOT_BRANCH, root_branch, :branch
|
78
|
+
set_config KEY_ROOT_REMOTE, root_remote, :branch
|
79
|
+
puts 'OK' if print_messages
|
80
|
+
end
|
53
81
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
if print_messages; print "Pulling #{root_branch}... " end
|
66
|
-
GitPivotalTrackerIntegration::Util::Shell.exec 'git pull --quiet --ff-only'
|
67
|
-
if print_messages; puts 'OK'
|
68
|
-
end
|
82
|
+
# Creates a commit with a given message. The commit includes all change
|
83
|
+
# files.
|
84
|
+
#
|
85
|
+
# @param [String] message The commit message, which will be appended with
|
86
|
+
# +[#<story-id]+
|
87
|
+
# @param [PivotalTracker::Story] story the story associated with the current
|
88
|
+
# commit
|
89
|
+
# @return [void]
|
90
|
+
def self.create_commit(message, story)
|
91
|
+
Util::Shell.exec "git commit --quiet --all --allow-empty --message \"#{message}\n\n[##{story.id}]\""
|
92
|
+
end
|
69
93
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
94
|
+
# Creates a tag with the given name. Before creating the tag, commits all
|
95
|
+
# outstanding changes with a commit message that reflects that these changes
|
96
|
+
# are for a release.
|
97
|
+
#
|
98
|
+
# @param [String] name the name of the tag to create
|
99
|
+
# @param [PivotalTracker::Story] story the story associated with the current
|
100
|
+
# tag
|
101
|
+
# @return [void]
|
102
|
+
def self.create_release_tag(name, story)
|
103
|
+
root_branch = branch_name
|
104
|
+
|
105
|
+
print "Creating tag v#{name}... "
|
106
|
+
|
107
|
+
create_branch RELEASE_BRANCH_NAME, false
|
108
|
+
create_commit "#{name} Release", story
|
109
|
+
Util::Shell.exec "git tag v#{name}"
|
110
|
+
Util::Shell.exec "git checkout --quiet #{root_branch}"
|
111
|
+
Util::Shell.exec "git branch --quiet -D #{RELEASE_BRANCH_NAME}"
|
112
|
+
|
113
|
+
puts 'OK'
|
114
|
+
end
|
77
115
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
116
|
+
# Returns a Git configuration value. This value is read using the +git
|
117
|
+
# config+ command. The scope of the value to read can be controlled with the
|
118
|
+
# +scope+ parameter.
|
119
|
+
#
|
120
|
+
# @param [String] key the key of the configuration to retrieve
|
121
|
+
# @param [:branch, :inherited] scope the scope to read the configuration from
|
122
|
+
# * +:branch+: equivalent to calling +git config branch.branch-name.key+
|
123
|
+
# * +:inherited+: equivalent to calling +git config key+
|
124
|
+
# @return [String] the value of the configuration
|
125
|
+
# @raise if the specified scope is not +:branch+ or +:inherited+
|
126
|
+
def self.get_config(key, scope = :inherited)
|
127
|
+
if :branch == scope
|
128
|
+
Util::Shell.exec("git config branch.#{branch_name}.#{key}", false).strip
|
129
|
+
elsif :inherited == scope
|
130
|
+
Util::Shell.exec("git config #{key}", false).strip
|
131
|
+
else
|
132
|
+
raise "Unable to get Git configuration for scope '#{scope}'"
|
133
|
+
end
|
134
|
+
end
|
89
135
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
puts 'OK'
|
110
|
-
end
|
136
|
+
# Merges the current branch to its root branch and deletes the current branch
|
137
|
+
#
|
138
|
+
# @param [PivotalTracker::Story] story the story associated with the current branch
|
139
|
+
# @param [Boolean] no_complete whether to suppress the +Completes+ statement in the commit message
|
140
|
+
# @return [void]
|
141
|
+
def self.merge(story, no_complete)
|
142
|
+
development_branch = branch_name
|
143
|
+
root_branch = get_config KEY_ROOT_BRANCH, :branch
|
144
|
+
|
145
|
+
print "Merging #{development_branch} to #{root_branch}... "
|
146
|
+
Util::Shell.exec "git checkout --quiet #{root_branch}"
|
147
|
+
Util::Shell.exec 'git pull --quiet --ff-only'
|
148
|
+
Util::Shell.exec "git merge --quiet --no-ff -m \"Merge #{development_branch} to #{root_branch}\n\n[#{no_complete ? '' : 'Completes '}##{story.id}]\" #{development_branch}"
|
149
|
+
puts 'OK'
|
150
|
+
|
151
|
+
print "Deleting #{development_branch}... "
|
152
|
+
Util::Shell.exec "git branch --quiet -D #{development_branch}"
|
153
|
+
puts 'OK'
|
154
|
+
end
|
111
155
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
# * +:branch+: equivalent to calling +git config branch.branch-name.key+
|
119
|
-
# * +:inherited+: equivalent to calling +git config key+
|
120
|
-
# @return [String] the value of the configuration
|
121
|
-
# @raise if the specified scope is not +:branch+ or +:inherited+
|
122
|
-
def self.get_config(key, scope = :inherited)
|
123
|
-
if :branch == scope
|
124
|
-
GitPivotalTrackerIntegration::Util::Shell.exec("git config branch.#{branch_name}.#{key}", false).strip
|
125
|
-
elsif :inherited == scope
|
126
|
-
GitPivotalTrackerIntegration::Util::Shell.exec("git config #{key}", false).strip
|
127
|
-
else
|
128
|
-
raise "Unable to get Git configuration for scope '#{scope}'"
|
129
|
-
end
|
130
|
-
end
|
156
|
+
# Push changes to the remote of the current branch
|
157
|
+
#
|
158
|
+
# @param [String] refs the explicit references to push
|
159
|
+
# @return [void]
|
160
|
+
def self.push(*refs)
|
161
|
+
remote = get_config KEY_REMOTE, :branch
|
131
162
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
# @return [void]
|
137
|
-
def self.merge(story, no_complete)
|
138
|
-
development_branch = branch_name
|
139
|
-
root_branch = get_config KEY_ROOT_BRANCH, :branch
|
140
|
-
|
141
|
-
print "Merging #{development_branch} to #{root_branch}... "
|
142
|
-
GitPivotalTrackerIntegration::Util::Shell.exec "git checkout --quiet #{root_branch}"
|
143
|
-
GitPivotalTrackerIntegration::Util::Shell.exec "git merge --quiet --no-ff -m \"Merge #{development_branch} to #{root_branch}\n\n[#{no_complete ? '' : 'Completes '}##{story.id}]\" #{development_branch}"
|
144
|
-
puts 'OK'
|
145
|
-
|
146
|
-
print "Deleting #{development_branch}... "
|
147
|
-
GitPivotalTrackerIntegration::Util::Shell.exec "git branch --quiet -D #{development_branch}"
|
148
|
-
puts 'OK'
|
149
|
-
end
|
163
|
+
print "Pushing to #{remote}... "
|
164
|
+
Util::Shell.exec "git push --quiet #{remote} " + refs.join(' ')
|
165
|
+
puts 'OK'
|
166
|
+
end
|
150
167
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
168
|
+
# Returns the root path of the current Git repository. The root is
|
169
|
+
# determined by ascending the path hierarchy, starting with the current
|
170
|
+
# working directory (+Dir#pwd+), until a directory is found that contains a
|
171
|
+
# +.git/+ sub directory.
|
172
|
+
#
|
173
|
+
# @return [String] the root path of the Git repository
|
174
|
+
# @raise if the current working directory is not in a Git repository
|
175
|
+
def self.repository_root
|
176
|
+
repository_root = Dir.pwd
|
177
|
+
|
178
|
+
until Dir.entries(repository_root).any? { |child| File.directory?(child) && (child =~ /^.git$/) }
|
179
|
+
next_repository_root = File.expand_path('..', repository_root)
|
180
|
+
abort('Current working directory is not in a Git repository') unless repository_root != next_repository_root
|
181
|
+
repository_root = next_repository_root
|
182
|
+
end
|
157
183
|
|
158
|
-
|
159
|
-
|
160
|
-
puts 'OK'
|
161
|
-
end
|
184
|
+
repository_root
|
185
|
+
end
|
162
186
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
187
|
+
# Sets a Git configuration value. This value is set using the +git config+
|
188
|
+
# command. The scope of the set value can be controlled with the +scope+
|
189
|
+
# parameter.
|
190
|
+
#
|
191
|
+
# @param [String] key the key of configuration to store
|
192
|
+
# @param [String] value the value of the configuration to store
|
193
|
+
# @param [:branch, :global, :local] scope the scope to store the configuration value in.
|
194
|
+
# * +:branch+: equivalent to calling +git config --local branch.branch-name.key value+
|
195
|
+
# * +:global+: equivalent to calling +git config --global key value+
|
196
|
+
# * +:local+: equivalent to calling +git config --local key value+
|
197
|
+
# @return [void]
|
198
|
+
# @raise if the specified scope is not +:branch+, +:global+, or +:local+
|
199
|
+
def self.set_config(key, value, scope = :local)
|
200
|
+
if :branch == scope
|
201
|
+
Util::Shell.exec "git config --local branch.#{branch_name}.#{key} #{value}"
|
202
|
+
elsif :global == scope
|
203
|
+
Util::Shell.exec "git config --global #{key} #{value}"
|
204
|
+
elsif :local == scope
|
205
|
+
Util::Shell.exec "git config --local #{key} #{value}"
|
206
|
+
else
|
207
|
+
raise "Unable to set Git configuration for scope '#{scope}'"
|
208
|
+
end
|
209
|
+
end
|
178
210
|
|
179
|
-
|
180
|
-
|
211
|
+
# Checks whether merging the current branch back to its root branch would be
|
212
|
+
# a trivial merge. A trivial merge is defined as one where the net change
|
213
|
+
# of the merge would be the same as the net change of the branch being
|
214
|
+
# merged. The easiest way to ensure that a merge is trivial is to rebase a
|
215
|
+
# development branch onto the tip of its root branch.
|
216
|
+
#
|
217
|
+
# @return [void]
|
218
|
+
def self.trivial_merge?
|
219
|
+
development_branch = branch_name
|
220
|
+
root_branch = get_config KEY_ROOT_BRANCH, :branch
|
181
221
|
|
182
|
-
|
183
|
-
# command. The scope of the set value can be controlled with the +scope+
|
184
|
-
# parameter.
|
185
|
-
#
|
186
|
-
# @param [String] key the key of configuration to store
|
187
|
-
# @param [String] value the value of the configuration to store
|
188
|
-
# @param [:branch, :global, :local] scope the scope to store the configuration value in.
|
189
|
-
# * +:branch+: equivalent to calling +git config --local branch.branch-name.key value+
|
190
|
-
# * +:global+: equivalent to calling +git config --global key value+
|
191
|
-
# * +:local+: equivalent to calling +git config --local key value+
|
192
|
-
# @return [void]
|
193
|
-
# @raise if the specified scope is not +:branch+, +:global+, or +:local+
|
194
|
-
def self.set_config(key, value, scope = :local)
|
195
|
-
if :branch == scope
|
196
|
-
GitPivotalTrackerIntegration::Util::Shell.exec "git config --local branch.#{branch_name}.#{key} #{value}"
|
197
|
-
elsif :global == scope
|
198
|
-
GitPivotalTrackerIntegration::Util::Shell.exec "git config --global #{key} #{value}"
|
199
|
-
elsif :local == scope
|
200
|
-
GitPivotalTrackerIntegration::Util::Shell.exec "git config --local #{key} #{value}"
|
201
|
-
else
|
202
|
-
raise "Unable to set Git configuration for scope '#{scope}'"
|
203
|
-
end
|
204
|
-
end
|
222
|
+
print "Checking for trivial merge from #{development_branch} to #{root_branch}... "
|
205
223
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
# merged. The easiest way to ensure that a merge is trivial is to rebase a
|
210
|
-
# development branch onto the tip of its root branch.
|
211
|
-
#
|
212
|
-
# @return [void]
|
213
|
-
def self.trivial_merge?
|
214
|
-
development_branch = branch_name
|
215
|
-
root_branch = get_config KEY_ROOT_BRANCH, :branch
|
224
|
+
Util::Shell.exec "git checkout --quiet #{root_branch}"
|
225
|
+
Util::Shell.exec 'git pull --quiet --ff-only'
|
226
|
+
Util::Shell.exec "git checkout --quiet #{development_branch}"
|
216
227
|
|
217
|
-
|
228
|
+
root_tip = Util::Shell.exec "git rev-parse #{root_branch}"
|
229
|
+
common_ancestor = Util::Shell.exec "git merge-base #{root_branch} #{development_branch}"
|
218
230
|
|
219
|
-
|
220
|
-
|
221
|
-
|
231
|
+
if root_tip != common_ancestor
|
232
|
+
abort "\n#{root_branch} branch is ahead of your #{development_branch} branch. \nSo please merge #{root_branch} to #{development_branch} and resolve any conflicts if any. Run 'git merge #{root_branch}' and try git finish again."
|
233
|
+
end
|
222
234
|
|
223
|
-
|
224
|
-
|
235
|
+
puts 'OK'
|
236
|
+
end
|
225
237
|
|
226
|
-
if root_tip != common_ancestor
|
227
|
-
abort "\n#{root_branch} branch is ahead of your #{development_branch} branch. \nSo please merge #{root_branch} to #{development_branch} and resolve any conflicts if any. Run 'git merge #{root_branch}' and try git finish again."
|
228
238
|
end
|
229
239
|
|
230
|
-
puts 'OK'
|
231
240
|
end
|
232
|
-
|
233
|
-
private
|
234
|
-
|
235
|
-
KEY_REMOTE = 'remote'.freeze
|
236
|
-
|
237
|
-
KEY_ROOT_BRANCH = 'root-branch'.freeze
|
238
|
-
|
239
|
-
KEY_ROOT_REMOTE = 'root-remote'.freeze
|
240
|
-
|
241
|
-
RELEASE_BRANCH_NAME = 'pivotal-tracker-release'.freeze
|
242
|
-
|
243
241
|
end
|
244
242
|
|