Added support for download command.

Change-Id: I3ebfa01e8f3454822170ee0d183de15dfb0b1f8a
This commit is contained in:
Monty Taylor 2011-10-02 15:50:42 -04:00
parent 57bbc40763
commit 08bd9cf1e8
3 changed files with 157 additions and 56 deletions

View File

@ -33,4 +33,6 @@ If you want to skip the automatic rebase -i step:
git review -R git review -R
If you want to download change 781 from gerrit to review it:
git review -d 781

View File

@ -43,6 +43,11 @@ OPTIONS
Skip cached local copies and force updates from network resources. Skip cached local copies and force updates from network resources.
.. options:: --download, -d
Download a change from gerrit into a branch for review. Takes a numeric
change id as an argument.
.. option:: --verbose, -v .. option:: --verbose, -v
Turns on more verbose output. Turns on more verbose output.

View File

@ -18,11 +18,16 @@ import commands
import optparse import optparse
import urllib import urllib
import json import json
from distutils.version import StrictVersion from distutils.version import StrictVersion
from urlparse import urlparse
import os import os
import sys import sys
import time import time
import re
version = "1.1" version = "1.1"
@ -32,16 +37,20 @@ CONFIGDIR = os.path.expanduser("~/.config/git-review")
PYPI_URL = "http://pypi.python.org/pypi/git-review/json" PYPI_URL = "http://pypi.python.org/pypi/git-review/json"
PYPI_CACHE_TIME = 60 * 60 * 24 # 24 hours PYPI_CACHE_TIME = 60 * 60 * 24 # 24 hours
def run_command(cmd, status=False): def run_command(cmd, status=False):
if VERBOSE: if VERBOSE:
print "Running:", cmd print "Running:", cmd
stat, out = commands.getstatusoutput(cmd) stat, out = commands.getstatusoutput(cmd)
if status: return (stat, out) if status:
return (stat, out)
return out return out
def run_command_status(cmd): def run_command_status(cmd):
return run_command(cmd, True) return run_command(cmd, True)
def update_latest_version(version_file_path): def update_latest_version(version_file_path):
""" Cache the latest version of git-review for the upgrade check. """ """ Cache the latest version of git-review for the upgrade check. """
@ -76,16 +85,19 @@ def latest_is_newer():
return False return False
def set_hooks_commit_msg(hostname="review.openstack.org"): def set_hooks_commit_msg():
""" Install the commit message hook if needed. """ """ Install the commit message hook if needed. """
top_dir = run_command('git rev-parse --show-toplevel') top_dir = run_command('git rev-parse --show-toplevel')
target_file = os.path.join(top_dir, ".git/hooks/commit-msg") target_file = os.path.join(top_dir, ".git/hooks/commit-msg")
source_location = "https://%s/tools/hooks/commit-msg" % hostname
if os.path.exists(target_file) and os.access(target_file, os.X_OK): if os.path.exists(target_file) and os.access(target_file, os.X_OK):
return return
(hostname, team, username, port, project_name) = \
parse_git_show("gerrit", "Push")
source_location = "https://%s/tools/hooks/commit-msg" % hostname
if not os.path.exists(target_file) or UPDATE: if not os.path.exists(target_file) or UPDATE:
if VERBOSE: if VERBOSE:
print "Fetching source_location: ", source_location print "Fetching source_location: ", source_location
@ -126,8 +138,6 @@ def add_remote(username, hostname, port, project):
def split_hostname(fetch_url): def split_hostname(fetch_url):
from urlparse import urlparse
parsed_url = urlparse(fetch_url) parsed_url = urlparse(fetch_url)
username = None username = None
hostname = parsed_url.netloc hostname = parsed_url.netloc
@ -169,25 +179,10 @@ def map_known_locations(hostname, team, project):
return hostname return hostname
def check_remote(remote): def parse_git_show(remote, verb):
"""Check that a Gerrit Git remote repo exists, if not, set one."""
if remote in run_command("git remote").split("\n"):
for current_remote in run_command("git branch -a").split("\n"):
if current_remote.strip() == "remotes/%s/master" % (remote) \
and not UPDATE:
return
# We have the remote, but aren't set up to fetch. Fix it
if VERBOSE:
print "Setting up gerrit branch tracking for better rebasing"
run_command("git remote update %s")
return
# Gerrit remote not present, try to add it
fetch_url = "" fetch_url = ""
for line in run_command("git remote show -n origin").split("\n"): for line in run_command("git remote show -n %s" % remote).split("\n"):
if line.strip().startswith("Fetch URL"): if line.strip().startswith("%s" % verb):
fetch_url = ":".join(line.split(":")[1:]).strip() fetch_url = ":".join(line.split(":")[1:]).strip()
project_name = fetch_url.split("/")[-1] project_name = fetch_url.split("/")[-1]
@ -200,7 +195,7 @@ def check_remote(remote):
port = None port = None
if VERBOSE: if VERBOSE:
print "Found origin fetch URL:", fetch_url print "Found origin %s URL:" % verb, fetch_url
# Special-case git@github urls - the rest can be parsed with urlparse # Special-case git@github urls - the rest can be parsed with urlparse
if fetch_url.startswith("git@github.com"): if fetch_url.startswith("git@github.com"):
@ -210,7 +205,33 @@ def check_remote(remote):
if hostname == "github.com": if hostname == "github.com":
team = fetch_url.split("/")[-2] team = fetch_url.split("/")[-2]
if team.startswith("git@github.com"):
team = team.split(':')[1]
return (hostname, team, username, port, project_name)
def check_remote(remote):
"""Check that a Gerrit Git remote repo exists, if not, set one."""
if remote in run_command("git remote").split("\n"):
for current_remote in run_command("git branch -a").split("\n"):
if current_remote.strip() == "remotes/%s/master" % (remote) \
and not UPDATE:
return
# We have the remote, but aren't set up to fetch. Fix it
if VERBOSE:
print "Setting up gerrit branch tracking for better rebasing"
output = run_command("git remote update %s")
if VERBOSE:
print output
return
(hostname, team, username, port, project_name) = \
parse_git_show("origin", "Fetch")
# Gerrit remote not present, try to add it
try: try:
(hostname, project) = map_known_locations(hostname, team, project_name) (hostname, project) = map_known_locations(hostname, team, project_name)
add_remote(username, hostname, port, project) add_remote(username, hostname, port, project)
@ -220,8 +241,6 @@ def check_remote(remote):
print "a remote named gerrit and try again." print "a remote named gerrit and try again."
raise raise
return hostname
def rebase_changes(branch, remote): def rebase_changes(branch, remote):
@ -249,23 +268,92 @@ def assert_diverge(branch, remote):
def get_topic(): def get_topic():
import re branch_name = None
for branch in run_command("git branch").split("\n"):
if branch.startswith('*'):
branch_name = branch.split()[1].strip()
branch_parts = branch_name.split("/")
if len(branch_parts) >= 3 and branch_parts[0] == "review":
return "/".join(branch_parts[2:])
log_output = run_command("git show --format='%s %b'") log_output = run_command("git show --format='%s %b'")
bug_re = r'\b([Bb]ug|[Ll][Pp])\s*[#:]?\s*(\d+)' bug_re = r'\b([Bb]ug|[Ll][Pp])\s*[#:]?\s*(\d+)'
match = re.search(bug_re, log_output) match = re.search(bug_re, log_output)
if match is not None: if match is not None:
return match.group(2) return "bug/%s" % match.group(2)
bp_re = r'\b([Bb]lue[Pp]rint|[Bb][Pp])\s*[#:]?\s*([0-9a-zA-Z-_]+)' bp_re = r'\b([Bb]lue[Pp]rint|[Bb][Pp])\s*[#:]?\s*([0-9a-zA-Z-_]+)'
match = re.search(bp_re, log_output) match = re.search(bp_re, log_output)
if match is not None: if match is not None:
return match.group(2) return "bp/%s" % match.group(2)
for branch in run_command("git branch").split("\n"): return branch_name
if branch.startswith('*'):
return branch.split()[1].strip()
def download_review(review):
(hostname, team, username, port, project_name) = \
parse_git_show("gerrit", "Push")
ssh_cmds = ["ssh"]
if port is not None:
ssh_cmds.extend(["-p", port])
if username is not None:
ssh_cmds.append(["-l", username])
ssh_cmd = " ".join(ssh_cmds)
query_string = "--format=JSON --current-patch-set change:%s" % review
review_info = None
(status, output) = run_command_status("%s gerrit query %s"
% (ssh_cmd, query_string))
if status != 0:
print "Could not fetch review information from gerrit"
print output
return status
try:
review_info = json.loads(output.split("\n")[0])
except:
if VERBOSE:
print output
print "Could not find a gerrit review with id: %s" % review
return 1
topic = review_info['topic']
if topic == "master":
topic = review
author = re.sub('\W+', '_', review_info['owner']['name']).lower()
branch_name = "review/%s/%s" % (author, topic)
revision = review_info['currentPatchSet']['revision']
refspec = review_info['currentPatchSet']['ref']
print "Downloading %s from gerrit into %s" % (refspec, branch_name)
checkout_cmd = "git checkout -b %s remotes/gerrit/master" % branch_name
(status, output) = run_command_status(checkout_cmd)
if status != 0:
if output.endswith("already exists"):
print "Branch already exists - reusing"
checkout_cmd = "git checkout %s" % branch_name
(status, output) = run_command_status(checkout_cmd)
if status != 0:
print output
return status
else:
print output
return status
(status, output) = run_command_status("git pull gerrit %s" % refspec)
if status != 0:
print output
return status
(status, output) = run_command_status("git reset --hard %s" % revision)
if status != 0:
print output
return status
return 0
def print_exit_message(status, needs_update): def print_exit_message(status, needs_update):
@ -299,6 +387,9 @@ def main():
help="Don't rebase changes before submitting.") help="Don't rebase changes before submitting.")
parser.add_option("-v", "--verbose", dest="verbose", action="store_true", parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help="Output more information about what's going on") help="Output more information about what's going on")
parser.add_option("-d", "--download", dest="download",
help="Download the contents of an existing gerrit "
"review into a branch")
parser.add_option("-u", "--update", dest="update", action="store_true", parser.add_option("-u", "--update", dest="update", action="store_true",
help="Force updates from remote locations") help="Force updates from remote locations")
parser.set_defaults(dry=False, rebase=True, verbose=False, update=False, parser.set_defaults(dry=False, rebase=True, verbose=False, update=False,
@ -314,6 +405,12 @@ def main():
UPDATE = options.update UPDATE = options.update
remote = options.remote remote = options.remote
needs_update = latest_is_newer()
check_remote(remote)
if options.download is not None:
print_exit_message(download_review(options.download), needs_update)
else:
topic = options.topic topic = options.topic
if topic is None: if topic is None:
topic = get_topic() topic = get_topic()
@ -325,11 +422,7 @@ def main():
drier = "echo -e Please use the following command " \ drier = "echo -e Please use the following command " \
"to send your commits to review:\n\n" "to send your commits to review:\n\n"
needs_update = latest_is_newer() set_hooks_commit_msg()
hostname = check_remote(remote)
set_hooks_commit_msg(hostname)
if UPDATE: if UPDATE:
cmd = "git fetch %s %s" % (remote, branch) cmd = "git fetch %s %s" % (remote, branch)
@ -340,7 +433,8 @@ def main():
print_exit_message(1, needs_update) print_exit_message(1, needs_update)
assert_diverge(branch, remote) assert_diverge(branch, remote)
cmd = "%s git push %s HEAD:refs/for/%s/%s" % (drier, remote, branch, topic) cmd = "%s git push %s HEAD:refs/for/%s/%s" % (drier, remote, branch,
topic)
(status, output) = run_command_status(cmd) (status, output) = run_command_status(cmd)
print output print output
print_exit_message(status, needs_update) print_exit_message(status, needs_update)