Permalink
Browse files

Handle git finish

  • Loading branch information...
1 parent 6a47d88 commit e8c0a28e152a53a9f1671d671370a12997070cd7 @jzempel committed Apr 12, 2012
Showing with 171 additions and 60 deletions.
  1. +10 −0 continuity/__init__.py
  2. +88 −53 continuity/cli.py
  3. +71 −7 continuity/git.py
  4. +1 −0 continuity/pt.py
  5. +1 −0 setup.py
View
10 continuity/__init__.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+"""
+ continuity
+ ~~~~~~~~~~
+
+ Continuity API.
+
+ :copyright: 2012 by Jonathan Zempel.
+ :license: BSD, see LICENSE for more details.
+"""
View
141 continuity/cli.py
@@ -15,15 +15,26 @@
from sys import argv, exit
+def _git():
+ """Get git.
+ """
+ try:
+ ret_val = Git()
+ except GitException:
+ print "Not a git repository."
+ exit(128)
+
+ return ret_val
+
+
def commit():
"""Git prepare commit message hook.
"""
if len(argv) >= 2:
git = Git()
- prefix = git.branch.name.split('-')[0]
try:
- story_id = int(prefix)
+ story_id = int(git.prefix)
with open(argv[1]) as file:
message = file.read()
@@ -38,10 +49,37 @@ def commit():
exit(0)
+def finish():
+ """Handle git request to finish a story branch.
+ """
+ git = _git()
+
+ try:
+ branch = git.branch.name
+ story_id = int(git.prefix)
+ message = "[finish #{0:d}]".format(story_id)
+ configuration = git.get_configuration("pivotal")
+
+ if configuration:
+ target = configuration.get("integration-branch")
+ else:
+ target = None
+
+ git.merge_branch(target, message)
+ print "Merged branch '{0}' into {1}.".format(branch, git.branch.name)
+ git.delete_branch(branch)
+ print "Deleted branch {0}.".format(branch)
+ git.push_branch()
+ print "Finished story #{0:d}.".format(story_id)
+ except ValueError:
+ print "Not a story branch."
+ exit(128)
+
+
def review():
"""Handle github branch pull request.
"""
- git = Git()
+ git = _git()
configuration = git.get_configuration("github")
if configuration:
@@ -54,16 +92,17 @@ def review():
try:
github = GitHub(git, user, password)
- title = ' '.join(git.branch.name.split('-')[1:])
+ title = '-'.join(git.branch.name.split('-')[1:])
message = "Pull request title [{0}]: ".format(title)
title = raw_input(message) or title
description = raw_input("Pull request description (optional): ")
- branch = configuration.get("branch")
+ git.push_branch()
+ branch = configuration.get("merge-branch")
pull_request = github.create_pull_request(title, description,
branch)
- print "Pull request opened: {0}".format(pull_request["url"])
+ print "Opened pull request: {0}".format(pull_request["url"])
except GitHubException, e:
- print e.message
+ print "Unable to create pull request."
exit(128)
else:
print "Missing 'github' git configuration."
@@ -72,54 +111,50 @@ def review():
def story():
"""Handle git branching and story state for the next story in PT.
"""
- try:
- git = Git()
- configuration = git.get_configuration("pivotal")
+ git = _git()
+ configuration = git.get_configuration("pivotal")
- if configuration:
- try:
- api_token = configuration["api-token"]
- project_id = configuration["project-id"]
- owner = configuration["owner"]
- except KeyError, e:
- print "Missing 'pivotal.{0}' git configuration.".\
- format(e.message)
- exit(1)
-
- pt = PivotalTracker(api_token, project_id)
- filter = "owner:{0} state:unstarted,rejected".format(owner)
- print "Retrieving next story from Pivotal Tracker for {0}".\
- format(owner)
+ if configuration:
+ try:
+ api_token = configuration["api-token"]
+ project_id = configuration["project-id"]
+ owner = configuration["owner"]
+ except KeyError, e:
+ print "Missing 'pivotal.{0}' git configuration.".\
+ format(e.message)
+ exit(1)
+
+ pt = PivotalTracker(api_token, project_id)
+ filter = "owner:{0} state:unstarted,rejected".format(owner)
+ print "Retrieving next story from Pivotal Tracker for {0}".\
+ format(owner)
+ story = pt.get_story(filter)
+
+ if not story:
+ filter = "state:unstarted"
story = pt.get_story(filter)
- if not story:
- filter = "state:unstarted"
- story = pt.get_story(filter)
-
- if story:
- print "Story: {0}".format(story.name)
-
- if story.owner != owner:
- story = pt.set_story(story.id, story.state, owner)
-
- # Verify that owner got the story.
- if story.owner == owner:
- message = "Enter branch name: {0:d}-".format(story.id)
-
- try:
- suffix = raw_input(message)
- name = "{0:d}-{1}".format(story.id, suffix)
- git.create_branch(name)
- pt.set_story(story.id, "started")
- print "Switched to a new branch '{0}'".format(name)
- except KeyboardInterrupt:
- print "\nCancelled story branch!"
- else:
- print "Unable to update story owner."
+ if story:
+ print "Story: {0}".format(story.name)
+
+ if story.owner != owner:
+ story = pt.set_story(story.id, story.state, owner)
+
+ # Verify that owner got the story.
+ if story.owner == owner:
+ message = "Enter branch name: {0:d}-".format(story.id)
+
+ try:
+ suffix = raw_input(message)
+ name = "{0:d}-{1}".format(story.id, suffix)
+ git.create_branch(name)
+ pt.set_story(story.id, "started")
+ print "Switched to a new branch '{0}'".format(name)
+ except KeyboardInterrupt:
+ print "\nCancelled story branch!"
else:
- print "No estimated stories found in the backlog."
+ print "Unable to update story owner."
else:
- print "Missing 'pivotal' git configuration."
- except GitException:
- print "Not a git repository."
- exit(128)
+ print "No estimated stories found in the backlog."
+ else:
+ print "Missing 'pivotal' git configuration."
View
78 continuity/git.py
@@ -43,20 +43,46 @@ def branch(self):
def create_branch(self, name, push=True):
"""Create the given branch name.
- :param name: The name of the branch to checkout.
+ :param name: The name of the branch to create.
:param push: Default `True`. Determine whether to push to remote.
"""
+ command = ["git", "checkout", "-b", str(name)]
+
try:
- command = ["git", "checkout", "-b", str(name)]
ret_val = self.repo.git.execute(command)
except GitCommandError:
- command = ["git", "checkout", str(name)]
- ret_val = self.repo.git.execute(command)
+ ret_val = self.get_branch(name)
if push:
- remote = self.repo.remotes.origin
- command = ["git", "push", remote.name, self.branch.name]
- self.repo.git.execute(command)
+ self.push_branch()
+
+ return ret_val
+
+ def delete_branch(self, name):
+ """Delete the given branch name.
+
+ :param name: The name of the branch to delete.
+ """
+ command = ["git", "branch", "-d", str(name)]
+
+ try:
+ ret_val = self.repo.git.execute(command)
+ except GitCommandError:
+ raise GitException(), None, exc_info()[2]
+
+ return ret_val
+
+ def get_branch(self, name):
+ """Get the given branch name.
+
+ :param name: The name of the branch to checkout.
+ """
+ command = ["git", "checkout", str(name)]
+
+ try:
+ ret_val = self.repo.git.execute(command)
+ except GitCommandError:
+ raise GitException("Invalid branch"), None, exc_info()[2]
return ret_val
@@ -76,6 +102,44 @@ def get_configuration(self, section):
return ret_val
+ def merge_branch(self, name=None, message=None):
+ """Merge the current branch into the given branch name.
+
+ :param name: Default `None`. The name of the branch to merge into.
+ :param message: Default `None`. An optional message to prepend to the
+ merge message.
+ """
+ commit = self.branch.name
+ self.get_branch(name or "master")
+
+ if message:
+ message = "{0} Merge branch '{1}' into {2}".format(message,
+ commit, self.branch.name)
+ command = ["git", "merge", "--no-ff", "-m", message, commit]
+ else:
+ command = ["git", "merge", commit]
+
+ return self.repo.git.execute(command)
+
+ def push_branch(self, name=None):
+ """Push the given branch name.
+
+ :param name: Default `None`. The name of the branch to push.
+ """
+ if name:
+ get_branch(name)
+
+ remote = self.repo.remotes.origin
+ command = ["git", "push", remote.name, self.branch.name]
+
+ return self.repo.git.execute(command)
+
+ @property
+ def prefix(self):
+ """Branch prefix accessor.
+ """
+ return self.branch.name.split('-')[0]
+
@property
def remote(self):
"""Remote accessor.
View
1 continuity/pt.py
@@ -133,6 +133,7 @@ def get_story(self, filter):
`https://www.pivotaltracker.com/help#howcanasearchberefined` for
details.
"""
+ filter = "type:feature,chore,bug {0}".format(filter)
stories = self.get_xml("stories", filter=filter, limit=1)
count = stories.attributes["count"]
View
1 setup.py
@@ -34,6 +34,7 @@
],
entry_points={
"console_scripts": [
+ "git-finish = continuity.cli:finish",
"git-review = continuity.cli:review",
"git-story = continuity.cli:story",
"prepare-commit-msg = continuity.cli:commit"

0 comments on commit e8c0a28

Please sign in to comment.