From 866276c2ecf2249c6327301e61390244def47b5c Mon Sep 17 00:00:00 2001 From: "bauerb@chromium.org" Date: Fri, 18 Mar 2011 20:09:31 +0000 Subject: [PATCH] Add support for wildcards in svn remote configuration. BUG=none TEST=manual Review URL: http://codereview.chromium.org/6598068 git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@78741 0039d316-1c4b-4281-b951-d872f2087c98 --- git_cl/git_cl.py | 69 +++++++++++++++++++++++++++++++----- git_cl/test/test-lib.sh | 11 ++++-- git_cl/test/upstream.sh | 32 +++++++++++++++++ scm.py | 78 ++++++++++++++++++++++++++++++++++++----- tests/scm_unittest.py | 13 ++++++- 5 files changed, 182 insertions(+), 21 deletions(-) create mode 100755 git_cl/test/upstream.sh diff --git a/git_cl/git_cl.py b/git_cl/git_cl.py index 56a2fa6cb8..218c9f3dae 100644 --- a/git_cl/git_cl.py +++ b/git_cl/git_cl.py @@ -101,6 +101,47 @@ def FixUrl(server): return server +def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards): + """Return the corresponding git ref if |base_url| together with |glob_spec| + matches the full |url|. + + If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below). + """ + fetch_suburl, as_ref = glob_spec.split(':') + if allow_wildcards: + glob_match = re.match('(.+/)?(\*|{[^/]*})(/.+)?', fetch_suburl) + if glob_match: + # Parse specs like "branches/*/src:refs/remotes/svn/*" or + # "branches/{472,597,648}/src:refs/remotes/svn/*". + branch_re = re.escape(base_url) + if glob_match.group(1): + branch_re += '/' + re.escape(glob_match.group(1)) + wildcard = glob_match.group(2) + if wildcard == '*': + branch_re += '([^/]*)' + else: + # Escape and replace surrounding braces with parentheses and commas + # with pipe symbols. + wildcard = re.escape(wildcard) + wildcard = re.sub('^\\\\{', '(', wildcard) + wildcard = re.sub('\\\\,', '|', wildcard) + wildcard = re.sub('\\\\}$', ')', wildcard) + branch_re += wildcard + if glob_match.group(3): + branch_re += re.escape(glob_match.group(3)) + match = re.match(branch_re, url) + if match: + return re.sub('\*$', match.group(1), as_ref) + + # Parse specs like "trunk/src:refs/remotes/origin/trunk". + if fetch_suburl: + full_url = base_url + '/' + fetch_suburl + else: + full_url = base_url + if full_url == url: + return as_ref + return None + class Settings(object): def __init__(self): self.default_server = None @@ -193,14 +234,26 @@ class Settings(object): remote = match.group(1) base_url = match.group(2) fetch_spec = RunGit( - ['config', 'svn-remote.'+remote+'.fetch']).strip().split(':') - if fetch_spec[0]: - full_url = base_url + '/' + fetch_spec[0] - else: - full_url = base_url - if full_url == url: - self.svn_branch = fetch_spec[1] - break + ['config', 'svn-remote.%s.fetch' % remote], + error_ok=True).strip() + if fetch_spec: + self.svn_branch = MatchSvnGlob(url, base_url, fetch_spec, False) + if self.svn_branch: + break + branch_spec = RunGit( + ['config', 'svn-remote.%s.branches' % remote], + error_ok=True).strip() + if branch_spec: + self.svn_branch = MatchSvnGlob(url, base_url, branch_spec, True) + if self.svn_branch: + break + tag_spec = RunGit( + ['config', 'svn-remote.%s.tags' % remote], + error_ok=True).strip() + if tag_spec: + self.svn_branch = MatchSvnGlob(url, base_url, tag_spec, True) + if self.svn_branch: + break if not self.svn_branch: DieWithError('Can\'t guess svn branch -- try specifying it on the ' diff --git a/git_cl/test/test-lib.sh b/git_cl/test/test-lib.sh index 7bcf25351f..139ddd59a8 100644 --- a/git_cl/test/test-lib.sh +++ b/git_cl/test/test-lib.sh @@ -5,6 +5,8 @@ set -e PWD=`pwd` REPO_URL=file://$PWD/svnrepo +TRUNK_URL=$REPO_URL/trunk +BRANCH_URL=$REPO_URL/branches/some_branch GITREPO_PATH=$PWD/gitrepo GITREPO_URL=file://$GITREPO_PATH GIT_CL=$PWD/../git-cl @@ -14,12 +16,13 @@ setup_initsvn() { echo "Setting up test SVN repo..." rm -rf svnrepo svnadmin create svnrepo - # Need this in order for Mac SnowLeopard to work echo "enable-rep-sharing = false" >> svnrepo/db/fsfs.conf + svn mkdir -q -m 'creating trunk' --parents $TRUNK_URL + rm -rf svn - svn co -q $REPO_URL svn + svn co -q $TRUNK_URL svn ( cd svn echo "test" > test @@ -28,6 +31,8 @@ setup_initsvn() { echo "test2" >> test svn commit -q -m "second commit" ) + + svn cp -q -m 'branching' --parents $TRUNK_URL $BRANCH_URL } # Set up a git-svn checkout of the repo. @@ -36,7 +41,7 @@ setup_gitsvn() { rm -rf git-svn # There appears to be no way to make git-svn completely shut up, so we # redirect its output. - git svn -q clone $REPO_URL git-svn >/dev/null 2>&1 + git svn -q clone -s $REPO_URL git-svn >/dev/null 2>&1 } # Set up a git repo that has a few commits to master. diff --git a/git_cl/test/upstream.sh b/git_cl/test/upstream.sh new file mode 100755 index 0000000000..0327b68227 --- /dev/null +++ b/git_cl/test/upstream.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# Check guessing the svn upstream branch. + +set -e + +. ./test-lib.sh + +setup_initsvn +setup_gitsvn + +( + set -e + cd git-svn + + git config rietveld.server localhost:8080 + + for ref in refs/remotes/trunk refs/remotes/some_branch; do + git checkout -q -B feature_branch $ref + test_expect_success "Guessing upstream branch for $ref" \ + "$GIT_CL upstream | egrep -q '^$ref$'" + git checkout -q master + done +) + +SUCCESS=$? + +cleanup + +if [ $SUCCESS == 0 ]; then + echo PASS +fi diff --git a/scm.py b/scm.py index 2156b293de..2564033698 100644 --- a/scm.py +++ b/scm.py @@ -157,6 +157,48 @@ class GIT(object): except gclient_utils.CheckCallError: return False + @staticmethod + def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards): + """Return the corresponding git ref if |base_url| together with |glob_spec| + matches the full |url|. + + If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below). + """ + fetch_suburl, as_ref = glob_spec.split(':') + if allow_wildcards: + glob_match = re.match('(.+/)?(\*|{[^/]*})(/.+)?', fetch_suburl) + if glob_match: + # Parse specs like "branches/*/src:refs/remotes/svn/*" or + # "branches/{472,597,648}/src:refs/remotes/svn/*". + branch_re = re.escape(base_url) + if glob_match.group(1): + branch_re += '/' + re.escape(glob_match.group(1)) + wildcard = glob_match.group(2) + if wildcard == '*': + branch_re += '([^/]*)' + else: + # Escape and replace surrounding braces with parentheses and commas + # with pipe symbols. + wildcard = re.escape(wildcard) + wildcard = re.sub('^\\\\{', '(', wildcard) + wildcard = re.sub('\\\\,', '|', wildcard) + wildcard = re.sub('\\\\}$', ')', wildcard) + branch_re += wildcard + if glob_match.group(3): + branch_re += re.escape(glob_match.group(3)) + match = re.match(branch_re, url) + if match: + return re.sub('\*$', match.group(1), as_ref) + + # Parse specs like "trunk/src:refs/remotes/origin/trunk". + if fetch_suburl: + full_url = base_url + '/' + fetch_suburl + else: + full_url = base_url + if full_url == url: + return as_ref + return None + @staticmethod def GetSVNBranch(cwd): """Returns the svn branch name if found.""" @@ -189,15 +231,33 @@ class GIT(object): if match: remote = match.group(1) base_url = match.group(2) - fetch_spec = GIT.Capture( - ['config', 'svn-remote.%s.fetch' % remote], - cwd=cwd).strip().split(':') - if fetch_spec[0]: - full_url = base_url + '/' + fetch_spec[0] - else: - full_url = base_url - if full_url == url: - return fetch_spec[1] + try: + fetch_spec = GIT.Capture( + ['config', 'svn-remote.%s.fetch' % remote], + cwd=cwd).strip() + branch = GIT.MatchSvnGlob(url, base_url, fetch_spec, False) + except gclient_utils.CheckCallError: + branch = None + if branch: + return branch + try: + branch_spec = GIT.Capture( + ['config', 'svn-remote.%s.branches' % remote], + cwd=cwd).strip() + branch = GIT.MatchSvnGlob(url, base_url, branch_spec, True) + except gclient_utils.CheckCallError: + branch = None + if branch: + return branch + try: + tag_spec = GIT.Capture( + ['config', 'svn-remote.%s.tags' % remote], + cwd=cwd).strip() + branch = GIT.MatchSvnGlob(url, base_url, tag_spec, True) + except gclient_utils.CheckCallError: + branch = None + if branch: + return branch @staticmethod def FetchUpstreamTuple(cwd): diff --git a/tests/scm_unittest.py b/tests/scm_unittest.py index 1b157e4e72..917e0e3bef 100755 --- a/tests/scm_unittest.py +++ b/tests/scm_unittest.py @@ -53,7 +53,7 @@ class GitWrapperTestCase(BaseSCMTestCase): 'FetchUpstreamTuple', 'GenerateDiff', 'GetBranch', 'GetBranchRef', 'GetCheckoutRoot', 'GetDifferentFiles', 'GetEmail', 'GetPatchName', 'GetSVNBranch', - 'GetUpstreamBranch', 'IsGitSvn', 'ShortBranchName', + 'GetUpstreamBranch', 'IsGitSvn', 'MatchSvnGlob', 'ShortBranchName', ] # If this test fails, you should add the relevant test. self.compareMembers(scm.GIT, members) @@ -65,6 +65,17 @@ class GitWrapperTestCase(BaseSCMTestCase): self.mox.ReplayAll() self.assertEqual(scm.GIT.GetEmail(self.root_dir), 'mini@me.com') + def testMatchSvnGlob(self): + self.assertEquals(scm.GIT.MatchSvnGlob( + 'svn://svn.chromium.org/chrome/trunk/src', + 'svn://svn.chromium.org/chrome', + 'trunk/src:refs/remotes/origin/trunk', + False), 'refs/remotes/origin/trunk') + self.assertEquals(scm.GIT.MatchSvnGlob( + 'https://v8.googlecode.com/svn/branches/bleeding_edge', + 'https://v8.googlecode.com/svn', + 'branches/*:refs/remotes/*', + True), 'refs/remotes/bleeding_edge') class SVNTestCase(BaseSCMTestCase): def setUp(self):