diff --git a/github/Repository.py b/github/Repository.py index 51a84fc4ad..a628c4a203 100644 --- a/github/Repository.py +++ b/github/Repository.py @@ -1644,6 +1644,27 @@ def get_branch(self, branch): ) return github.Branch.Branch(self._requester, headers, data, completed=True) + def rename_branch(self, branch, new_name): + """ + :calls: `POST /repos/{owner}/{repo}/branches/{branch}/rename ` + :param branch: :class:`github.Branch.Branch` or string + :param new_name: string + :rtype: bool + + NOTE: This method does not return the branch since it may take some + time to fully complete server-side. + """ + is_branch = isinstance(branch, github.Branch.Branch) + assert isinstance(branch, str) or is_branch, branch + assert isinstance(new_name, str), new_name + if is_branch: + branch = branch.name + parameters = {"new_name": new_name} + status, _, _ = self._requester.requestJson( + "POST", f"{self.url}/branches/{branch}/rename", input=parameters + ) + return status == 201 + def get_branches(self): """ :calls: `GET /repos/{owner}/{repo}/branches `_ diff --git a/github/Repository.pyi b/github/Repository.pyi index fd876a363a..eb0dffdd14 100644 --- a/github/Repository.pyi +++ b/github/Repository.pyi @@ -307,6 +307,9 @@ class Repository(CompletableGithubObject): ) -> str: ... def get_assignees(self) -> PaginatedList[NamedUser]: ... def get_branch(self, branch: str) -> Branch: ... + def rename_branch( + self, branch: Union[str, Branch], new_name: str + ) -> bool: ... def get_branches(self) -> PaginatedList[Branch]: ... def get_check_run(self, check_run_id: int) -> CheckRun: ... def get_check_suite(self, check_suite_id: int) -> CheckSuite: ... diff --git a/tests/ReplayData/Repository.testRenameBranchObject.txt b/tests/ReplayData/Repository.testRenameBranchObject.txt new file mode 100644 index 0000000000..23abea565e --- /dev/null +++ b/tests/ReplayData/Repository.testRenameBranchObject.txt @@ -0,0 +1,22 @@ +https +GET +api.github.com +None +/repos/jacquev6/PyGithub/branches/neat-new-feature +{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'} +None +200 +[('Server', 'GitHub.com'), ('Date', 'Sat, 23 Oct 2021 04:32:06 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Transfer-Encoding', 'chunked'), ('Cache-Control', 'private, max-age=60, s-maxage=60'), ('Vary', 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding, Accept, X-Requested-With'), ('ETag', 'W/"7b4b9377dbba34db4d606bcfaadf8997f690d2fb7afc15982857269ba17e8a90"'), ('X-OAuth-Scopes', 'admin:enterprise, admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete:packages, delete_repo, gist, notifications, repo, user, workflow, write:discussion, write:packages'), ('X-Accepted-OAuth-Scopes', ''), ('X-GitHub-Media-Type', 'github.v3; format=json'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4998'), ('X-RateLimit-Reset', '1634967125'), ('X-RateLimit-Used', '2'), ('X-RateLimit-Resource', 'core'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '0'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('Content-Encoding', 'gzip'), ('X-GitHub-Request-Id', '82FE:7C71:69CE0C:7107D9:61739046')] +{"name":"neat-new-feature","commit":{"sha":"dbfbf7f07b81d616d4da032bcfa7ca90dcd79575","node_id":"C_kwDOB7W4ZNoAKGRiZmJmN2YwN2I4MWQ2MTZkNGRhMDMyYmNmYTdjYTkwZGNkNzk1NzU","commit":{"author":{"name":"Steve Kowalik","email":"steven@wedontsleep.org","date":"2021-10-21T04:06:06Z"},"committer":{"name":"Steve Kowalik","email":"steven@wedontsleep.org","date":"2021-10-21T04:06:06Z"},"message":"WIP: branch rename","tree":{"sha":"81e30fe50ff2b1c8e8cb819d181b3a285e9a9c94","url":"https://api.github.com/repos/jacquev6/PyGithub/git/trees/81e30fe50ff2b1c8e8cb819d181b3a285e9a9c94"},"url":"https://api.github.com/repos/jacquev6/PyGithub/git/commits/dbfbf7f07b81d616d4da032bcfa7ca90dcd79575","comment_count":0,"verification":{"verified":false,"reason":"unsigned","signature":null,"payload":null}},"url":"https://api.github.com/repos/jacquev6/PyGithub/commits/dbfbf7f07b81d616d4da032bcfa7ca90dcd79575","html_url":"https://github.com/jacquev6/PyGithub/commit/dbfbf7f07b81d616d4da032bcfa7ca90dcd79575","comments_url":"https://api.github.com/repos/jacquev6/PyGithub/commits/dbfbf7f07b81d616d4da032bcfa7ca90dcd79575/comments","author":{"login":"jacquev6","id":15225059,"node_id":"MDQ6VXNlcjE1MjI1MDU5","avatar_url":"https://avatars.githubusercontent.com/u/15225059?v=4","gravatar_id":"","url":"https://api.github.com/users/jacquev6","html_url":"https://github.com/jacquev6","followers_url":"https://api.github.com/users/jacquev6/followers","following_url":"https://api.github.com/users/jacquev6/following{/other_user}","gists_url":"https://api.github.com/users/jacquev6/gists{/gist_id}","starred_url":"https://api.github.com/users/jacquev6/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jacquev6/subscriptions","organizations_url":"https://api.github.com/users/jacquev6/orgs","repos_url":"https://api.github.com/users/jacquev6/repos","events_url":"https://api.github.com/users/jacquev6/events{/privacy}","received_events_url":"https://api.github.com/users/jacquev6/received_events","type":"User","site_admin":false},"committer":{"login":"jacquev6","id":15225059,"node_id":"MDQ6VXNlcjE1MjI1MDU5","avatar_url":"https://avatars.githubusercontent.com/u/15225059?v=4","gravatar_id":"","url":"https://api.github.com/users/jacquev6","html_url":"https://github.com/jacquev6","followers_url":"https://api.github.com/users/jacquev6/followers","following_url":"https://api.github.com/users/jacquev6/following{/other_user}","gists_url":"https://api.github.com/users/jacquev6/gists{/gist_id}","starred_url":"https://api.github.com/users/jacquev6/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jacquev6/subscriptions","organizations_url":"https://api.github.com/users/jacquev6/orgs","repos_url":"https://api.github.com/users/jacquev6/repos","events_url":"https://api.github.com/users/jacquev6/events{/privacy}","received_events_url":"https://api.github.com/users/jacquev6/received_events","type":"User","site_admin":false},"parents":[{"sha":"93b92cd2fce560080dc66aef3b94427623046c5d","url":"https://api.github.com/repos/jacquev6/PyGithub/commits/93b92cd2fce560080dc66aef3b94427623046c5d","html_url":"https://github.com/jacquev6/PyGithub/commit/93b92cd2fce560080dc66aef3b94427623046c5d"}]},"_links":{"self":"https://api.github.com/repos/jacquev6/PyGithub/branches/neat-new-feature","html":"https://github.com/jacquev6/PyGithub/tree/neat-new-feature"},"protected":false,"protection":{"enabled":false,"required_status_checks":{"enforcement_level":"off","contexts":[]}},"protection_url":"https://api.github.com/repos/jacquev6/PyGithub/branches/neat-new-feature/protection"} + +https +POST +api.github.com +None +/repos/jacquev6/PyGithub/branches/neat-new-feature/rename +{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json'} +{"new_name": "terrible-idea"} +201 +[('Server', 'GitHub.com'), ('Date', 'Sat, 23 Oct 2021 04:32:07 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Content-Length', '3748'), ('Cache-Control', 'private, max-age=60, s-maxage=60'), ('Vary', 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding, Accept, X-Requested-With'), ('ETag', '"21b924f024389f2dad56ec7b61664d88fa432e59b6068797da15b3b1718799a1"'), ('X-OAuth-Scopes', 'admin:enterprise, admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete:packages, delete_repo, gist, notifications, repo, user, workflow, write:discussion, write:packages'), ('X-Accepted-OAuth-Scopes', ''), ('X-GitHub-Media-Type', 'github.v3; format=json'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4996'), ('X-RateLimit-Reset', '1634967125'), ('X-RateLimit-Used', '4'), ('X-RateLimit-Resource', 'core'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '0'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('X-GitHub-Request-Id', '8302:7C71:69CE3F:710812:61739046')] +{"name":"terrible-idea","commit":{"sha":"dbfbf7f07b81d616d4da032bcfa7ca90dcd79575","node_id":"C_kwDOB7W4ZNoAKGRiZmJmN2YwN2I4MWQ2MTZkNGRhMDMyYmNmYTdjYTkwZGNkNzk1NzU","commit":{"author":{"name":"Steve Kowalik","email":"steven@wedontsleep.org","date":"2021-10-21T04:06:06Z"},"committer":{"name":"Steve Kowalik","email":"steven@wedontsleep.org","date":"2021-10-21T04:06:06Z"},"message":"WIP: branch rename","tree":{"sha":"81e30fe50ff2b1c8e8cb819d181b3a285e9a9c94","url":"https://api.github.com/repos/jacquev6/PyGithub/git/trees/81e30fe50ff2b1c8e8cb819d181b3a285e9a9c94"},"url":"https://api.github.com/repos/jacquev6/PyGithub/git/commits/dbfbf7f07b81d616d4da032bcfa7ca90dcd79575","comment_count":0,"verification":{"verified":false,"reason":"unsigned","signature":null,"payload":null}},"url":"https://api.github.com/repos/jacquev6/PyGithub/commits/dbfbf7f07b81d616d4da032bcfa7ca90dcd79575","html_url":"https://github.com/jacquev6/PyGithub/commit/dbfbf7f07b81d616d4da032bcfa7ca90dcd79575","comments_url":"https://api.github.com/repos/jacquev6/PyGithub/commits/dbfbf7f07b81d616d4da032bcfa7ca90dcd79575/comments","author":{"login":"jacquev6","id":15225059,"node_id":"MDQ6VXNlcjE1MjI1MDU5","avatar_url":"https://avatars.githubusercontent.com/u/15225059?v=4","gravatar_id":"","url":"https://api.github.com/users/jacquev6","html_url":"https://github.com/jacquev6","followers_url":"https://api.github.com/users/jacquev6/followers","following_url":"https://api.github.com/users/jacquev6/following{/other_user}","gists_url":"https://api.github.com/users/jacquev6/gists{/gist_id}","starred_url":"https://api.github.com/users/jacquev6/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jacquev6/subscriptions","organizations_url":"https://api.github.com/users/jacquev6/orgs","repos_url":"https://api.github.com/users/jacquev6/repos","events_url":"https://api.github.com/users/jacquev6/events{/privacy}","received_events_url":"https://api.github.com/users/jacquev6/received_events","type":"User","site_admin":false},"committer":{"login":"jacquev6","id":15225059,"node_id":"MDQ6VXNlcjE1MjI1MDU5","avatar_url":"https://avatars.githubusercontent.com/u/15225059?v=4","gravatar_id":"","url":"https://api.github.com/users/jacquev6","html_url":"https://github.com/jacquev6","followers_url":"https://api.github.com/users/jacquev6/followers","following_url":"https://api.github.com/users/jacquev6/following{/other_user}","gists_url":"https://api.github.com/users/jacquev6/gists{/gist_id}","starred_url":"https://api.github.com/users/jacquev6/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jacquev6/subscriptions","organizations_url":"https://api.github.com/users/jacquev6/orgs","repos_url":"https://api.github.com/users/jacquev6/repos","events_url":"https://api.github.com/users/jacquev6/events{/privacy}","received_events_url":"https://api.github.com/users/jacquev6/received_events","type":"User","site_admin":false},"parents":[{"sha":"93b92cd2fce560080dc66aef3b94427623046c5d","url":"https://api.github.com/repos/jacquev6/PyGithub/commits/93b92cd2fce560080dc66aef3b94427623046c5d","html_url":"https://github.com/jacquev6/PyGithub/commit/93b92cd2fce560080dc66aef3b94427623046c5d"}]},"_links":{"self":"https://api.github.com/repos/jacquev6/PyGithub/branches/terrible-idea","html":"https://github.com/jacquev6/PyGithub/tree/terrible-idea"},"protected":false,"protection":{"enabled":false,"required_status_checks":{"enforcement_level":"off","contexts":[]}},"protection_url":"https://api.github.com/repos/jacquev6/PyGithub/branches/terrible-idea/protection"} + diff --git a/tests/ReplayData/Repository.testRenameBranchString.txt b/tests/ReplayData/Repository.testRenameBranchString.txt new file mode 100644 index 0000000000..193c642eda --- /dev/null +++ b/tests/ReplayData/Repository.testRenameBranchString.txt @@ -0,0 +1,11 @@ +https +POST +api.github.com +None +/repos/jacquev6/PyGithub/branches/neat-new-feature/rename +{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json'} +{"new_name": "terrible-idea"} +201 +[('Server', 'GitHub.com'), ('Date', 'Sat, 23 Oct 2021 04:32:07 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Content-Length', '3748'), ('Cache-Control', 'private, max-age=60, s-maxage=60'), ('Vary', 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding, Accept, X-Requested-With'), ('ETag', '"21b924f024389f2dad56ec7b61664d88fa432e59b6068797da15b3b1718799a1"'), ('X-OAuth-Scopes', 'admin:enterprise, admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete:packages, delete_repo, gist, notifications, repo, user, workflow, write:discussion, write:packages'), ('X-Accepted-OAuth-Scopes', ''), ('X-GitHub-Media-Type', 'github.v3; format=json'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4996'), ('X-RateLimit-Reset', '1634967125'), ('X-RateLimit-Used', '4'), ('X-RateLimit-Resource', 'core'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '0'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('X-GitHub-Request-Id', '8302:7C71:69CE3F:710812:61739046')] +{"name":"terrible-idea","commit":{"sha":"dbfbf7f07b81d616d4da032bcfa7ca90dcd79575","node_id":"C_kwDOB7W4ZNoAKGRiZmJmN2YwN2I4MWQ2MTZkNGRhMDMyYmNmYTdjYTkwZGNkNzk1NzU","commit":{"author":{"name":"Steve Kowalik","email":"steven@wedontsleep.org","date":"2021-10-21T04:06:06Z"},"committer":{"name":"Steve Kowalik","email":"steven@wedontsleep.org","date":"2021-10-21T04:06:06Z"},"message":"WIP: branch rename","tree":{"sha":"81e30fe50ff2b1c8e8cb819d181b3a285e9a9c94","url":"https://api.github.com/repos/jacquev6/PyGithub/git/trees/81e30fe50ff2b1c8e8cb819d181b3a285e9a9c94"},"url":"https://api.github.com/repos/jacquev6/PyGithub/git/commits/dbfbf7f07b81d616d4da032bcfa7ca90dcd79575","comment_count":0,"verification":{"verified":false,"reason":"unsigned","signature":null,"payload":null}},"url":"https://api.github.com/repos/jacquev6/PyGithub/commits/dbfbf7f07b81d616d4da032bcfa7ca90dcd79575","html_url":"https://github.com/jacquev6/PyGithub/commit/dbfbf7f07b81d616d4da032bcfa7ca90dcd79575","comments_url":"https://api.github.com/repos/jacquev6/PyGithub/commits/dbfbf7f07b81d616d4da032bcfa7ca90dcd79575/comments","author":{"login":"jacquev6","id":15225059,"node_id":"MDQ6VXNlcjE1MjI1MDU5","avatar_url":"https://avatars.githubusercontent.com/u/15225059?v=4","gravatar_id":"","url":"https://api.github.com/users/jacquev6","html_url":"https://github.com/jacquev6","followers_url":"https://api.github.com/users/jacquev6/followers","following_url":"https://api.github.com/users/jacquev6/following{/other_user}","gists_url":"https://api.github.com/users/jacquev6/gists{/gist_id}","starred_url":"https://api.github.com/users/jacquev6/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jacquev6/subscriptions","organizations_url":"https://api.github.com/users/jacquev6/orgs","repos_url":"https://api.github.com/users/jacquev6/repos","events_url":"https://api.github.com/users/jacquev6/events{/privacy}","received_events_url":"https://api.github.com/users/jacquev6/received_events","type":"User","site_admin":false},"committer":{"login":"jacquev6","id":15225059,"node_id":"MDQ6VXNlcjE1MjI1MDU5","avatar_url":"https://avatars.githubusercontent.com/u/15225059?v=4","gravatar_id":"","url":"https://api.github.com/users/jacquev6","html_url":"https://github.com/jacquev6","followers_url":"https://api.github.com/users/jacquev6/followers","following_url":"https://api.github.com/users/jacquev6/following{/other_user}","gists_url":"https://api.github.com/users/jacquev6/gists{/gist_id}","starred_url":"https://api.github.com/users/jacquev6/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jacquev6/subscriptions","organizations_url":"https://api.github.com/users/jacquev6/orgs","repos_url":"https://api.github.com/users/jacquev6/repos","events_url":"https://api.github.com/users/jacquev6/events{/privacy}","received_events_url":"https://api.github.com/users/jacquev6/received_events","type":"User","site_admin":false},"parents":[{"sha":"93b92cd2fce560080dc66aef3b94427623046c5d","url":"https://api.github.com/repos/jacquev6/PyGithub/commits/93b92cd2fce560080dc66aef3b94427623046c5d","html_url":"https://github.com/jacquev6/PyGithub/commit/93b92cd2fce560080dc66aef3b94427623046c5d"}]},"_links":{"self":"https://api.github.com/repos/jacquev6/PyGithub/branches/terrible-idea","html":"https://github.com/jacquev6/PyGithub/tree/terrible-idea"},"protected":false,"protection":{"enabled":false,"required_status_checks":{"enforcement_level":"off","contexts":[]}},"protection_url":"https://api.github.com/repos/jacquev6/PyGithub/branches/terrible-idea/protection"} + diff --git a/tests/Repository.py b/tests/Repository.py index e30aa884c8..564b8b1146 100644 --- a/tests/Repository.py +++ b/tests/Repository.py @@ -1323,6 +1323,13 @@ def testGetBranch(self): branch = self.repo.get_branch("develop") self.assertEqual(branch.commit.sha, "03058a36164d2a7d946db205f25538434fa27d94") + def testRenameBranchObject(self): + branch = self.repo.get_branch("neat-new-feature") + self.assertTrue(self.repo.rename_branch(branch, "terrible-idea")) + + def testRenameBranchString(self): + self.assertTrue(self.repo.rename_branch("neat-new-feature", "terrible-idea")) + def testMergeWithoutMessage(self): commit = self.repo.merge("branchForBase", "branchForHead") self.assertEqual(