Skip to content

Commit 6b1dd8b

Browse files
committed
Adding support for git remote set-url/get-url API to Remote
Both commands enable handling of a little known feature of git, which is to support multiple URL for one remote. You can add multiple url using the `set_url` subcommand of `git remote`. As listing them is also handy, there's a nice method to do it, using `get_url`. * adding set_url method that maps to the git remote set-url command¶ * can be used to set an URL, or replace an URL with optional positional arg¶ * can be used to add, delete URL with kwargs (matching set-url options)¶ * adding add_url, delete_url methods that wraps around set_url for conveniency¶ * adding urls property that yields an iterator over the setup urls for a remote¶ * adding a test suite that checks all use case scenarii of this added API.¶ Signed-off-by: Guyzmo <guyzmo+github@m0g.net>
1 parent e0b21f4 commit 6b1dd8b

File tree

2 files changed

+95
-2
lines changed

2 files changed

+95
-2
lines changed

git/remote.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,54 @@ def iter_items(cls, repo):
452452
yield Remote(repo, section[lbound + 1:rbound])
453453
# END for each configuration section
454454

455+
def set_url(self, new_url, old_url=None, **kwargs):
456+
"""Configure URLs on current remote (cf command git remote set_url)
457+
458+
This command manages URLs on the remote.
459+
460+
:param new_url: string being the URL to add as an extra remote URL
461+
:param old_url: when set, replaces this URL with new_url for the remote
462+
:return: self
463+
"""
464+
scmd = 'set-url'
465+
kwargs['insert_kwargs_after'] = scmd
466+
if old_url:
467+
self.repo.git.remote(scmd, self.name, old_url, new_url, **kwargs)
468+
else:
469+
self.repo.git.remote(scmd, self.name, new_url, **kwargs)
470+
return self
471+
472+
def add_url(self, url, **kwargs):
473+
"""Adds a new url on current remote (special case of git remote set_url)
474+
475+
This command adds new URLs to a given remote, making it possible to have
476+
multiple URLs for a single remote.
477+
478+
:param url: string being the URL to add as an extra remote URL
479+
:return: self
480+
"""
481+
return self.set_url(url, add=True)
482+
483+
def delete_url(self, url, **kwargs):
484+
"""Deletes a new url on current remote (special case of git remote set_url)
485+
486+
This command deletes new URLs to a given remote, making it possible to have
487+
multiple URLs for a single remote.
488+
489+
:param url: string being the URL to delete from the remote
490+
:return: self
491+
"""
492+
return self.set_url(url, delete=True)
493+
494+
@property
495+
def urls(self):
496+
""":return: Iterator yielding all configured URL targets on a remote
497+
as strings"""
498+
scmd = 'get-url'
499+
kwargs = {'insert_kwargs_after': scmd}
500+
for url in self.repo.git.remote(scmd, self.name, all=True, **kwargs).split('\n'):
501+
yield url
502+
455503
@property
456504
def refs(self):
457505
"""

git/test/test_remote.py

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
with_rw_repo,
1010
with_rw_and_rw_remote_repo,
1111
fixture,
12-
GIT_DAEMON_PORT
12+
GIT_DAEMON_PORT,
13+
assert_raises
1314
)
1415
from git import (
1516
RemoteProgress,
@@ -62,7 +63,7 @@ def update(self, op_code, cur_count, max_count=None, message=''):
6263
# check each stage only comes once
6364
op_id = op_code & self.OP_MASK
6465
assert op_id in (self.COUNTING, self.COMPRESSING, self.WRITING)
65-
66+
6667
if op_code & self.WRITING > 0:
6768
if op_code & self.BEGIN > 0:
6869
assert not message, 'should not have message when remote begins writing'
@@ -568,3 +569,47 @@ def test_uncommon_branch_names(self):
568569
assert res[0].remote_ref_path == 'refs/pull/1/head'
569570
assert res[0].ref.path == 'refs/heads/pull/1/head'
570571
assert isinstance(res[0].ref, Head)
572+
573+
@with_rw_repo('HEAD', bare=False)
574+
def test_multiple_urls(self, rw_repo):
575+
# test addresses
576+
test1 = 'https://github.com/gitpython-developers/GitPython'
577+
test2 = 'https://github.com/gitpython-developers/gitdb'
578+
test3 = 'https://github.com/gitpython-developers/smmap'
579+
580+
remote = rw_repo.remotes[0]
581+
# Testing setting a single URL
582+
remote.set_url(test1)
583+
assert list(remote.urls) == [test1]
584+
585+
# Testing replacing that single URL
586+
remote.set_url(test1)
587+
assert list(remote.urls) == [test1]
588+
# Testing adding new URLs
589+
remote.set_url(test2, add=True)
590+
assert list(remote.urls) == [test1, test2]
591+
remote.set_url(test3, add=True)
592+
assert list(remote.urls) == [test1, test2, test3]
593+
# Testing removing an URL
594+
remote.set_url(test2, delete=True)
595+
assert list(remote.urls) == [test1, test3]
596+
# Testing changing an URL
597+
remote.set_url(test3, test2)
598+
assert list(remote.urls) == [test1, test2]
599+
600+
# will raise: fatal: --add --delete doesn't make sense
601+
assert_raises(GitCommandError, remote.set_url, test2, add=True, delete=True)
602+
603+
# Testing on another remote, with the add/delete URL
604+
remote = rw_repo.create_remote('another', url=test1)
605+
remote.add_url(test2)
606+
assert list(remote.urls) == [test1, test2]
607+
remote.add_url(test3)
608+
assert list(remote.urls) == [test1, test2, test3]
609+
# Testing removing all the URLs
610+
remote.delete_url(test2)
611+
assert list(remote.urls) == [test1, test3]
612+
remote.delete_url(test1)
613+
assert list(remote.urls) == [test3]
614+
# will raise fatal: Will not delete all non-push URLs
615+
assert_raises(GitCommandError, remote.delete_url, test3)

0 commit comments

Comments
 (0)