4
4
5
5
import datetime
6
6
import pathlib
7
+ import re
7
8
import shlex
8
9
import typing as t
9
10
from collections .abc import Sequence
@@ -2361,12 +2362,16 @@ class GitRemoteCmd:
2361
2362
"""Run commands directly for a git remote on a git repository."""
2362
2363
2363
2364
remote_name : str
2365
+ fetch_url : str | None
2366
+ push_url : str | None
2364
2367
2365
2368
def __init__ (
2366
2369
self ,
2367
2370
* ,
2368
2371
path : StrPath ,
2369
2372
remote_name : str ,
2373
+ fetch_url : str | None = None ,
2374
+ push_url : str | None = None ,
2370
2375
cmd : Git | None = None ,
2371
2376
) -> None :
2372
2377
r"""Lite, typed, pythonic wrapper for git-remote(1).
@@ -2408,6 +2413,8 @@ def __init__(
2408
2413
self .cmd = cmd if isinstance (cmd , Git ) else Git (path = self .path )
2409
2414
2410
2415
self .remote_name = remote_name
2416
+ self .fetch_url = fetch_url
2417
+ self .push_url = push_url
2411
2418
2412
2419
def __repr__ (self ) -> str :
2413
2420
"""Representation of a git remote for a git repository."""
@@ -2731,6 +2738,24 @@ def set_url(
2731
2738
)
2732
2739
2733
2740
2741
+ GitRemoteManagerLiteral = Literal [
2742
+ "--verbose" ,
2743
+ "add" ,
2744
+ "rename" ,
2745
+ "remove" ,
2746
+ "set-branches" ,
2747
+ "set-head" ,
2748
+ "set-branch" ,
2749
+ "get-url" ,
2750
+ "set-url" ,
2751
+ "set-url --add" ,
2752
+ "set-url --delete" ,
2753
+ "prune" ,
2754
+ "show" ,
2755
+ "update" ,
2756
+ ]
2757
+
2758
+
2734
2759
class GitRemoteManager :
2735
2760
"""Run commands directly related to git remotes of a git repo."""
2736
2761
@@ -2777,7 +2802,7 @@ def __repr__(self) -> str:
2777
2802
2778
2803
def run (
2779
2804
self ,
2780
- command : GitRemoteCommandLiteral | None = None ,
2805
+ command : GitRemoteManagerLiteral | None = None ,
2781
2806
local_flags : list [str ] | None = None ,
2782
2807
* ,
2783
2808
# Pass-through to run()
@@ -2860,6 +2885,16 @@ def show(
2860
2885
--------
2861
2886
>>> GitRemoteManager(path=example_git_repo.path).show()
2862
2887
'origin'
2888
+
2889
+ For the example below, add a remote:
2890
+ >>> GitRemoteManager(path=example_git_repo.path).add(
2891
+ ... name='my_remote', url=f'file:///dev/null'
2892
+ ... )
2893
+ ''
2894
+
2895
+ Retrieve a list of remote names:
2896
+ >>> GitRemoteManager(path=example_git_repo.path).show().splitlines()
2897
+ ['my_remote', 'origin']
2863
2898
"""
2864
2899
local_flags : list [str ] = []
2865
2900
required_flags : list [str ] = []
@@ -2880,17 +2915,17 @@ def show(
2880
2915
log_in_real_time = log_in_real_time ,
2881
2916
)
2882
2917
2883
- def _ls (self ) -> list [ str ] :
2884
- """List remotes.
2918
+ def _ls (self ) -> str :
2919
+ r """List remotes (raw output) .
2885
2920
2886
2921
Examples
2887
2922
--------
2888
2923
>>> GitRemoteManager(path=example_git_repo.path)._ls()
2889
- [ 'origin']
2924
+ 'origin\tfile:///... (fetch)\norigin\tfile:///... (push)'
2890
2925
"""
2891
2926
return self .run (
2892
- "show " ,
2893
- ). splitlines ()
2927
+ "--verbose " ,
2928
+ )
2894
2929
2895
2930
def ls (self ) -> QueryList [GitRemoteCmd ]:
2896
2931
"""List remotes.
@@ -2899,14 +2934,56 @@ def ls(self) -> QueryList[GitRemoteCmd]:
2899
2934
--------
2900
2935
>>> GitRemoteManager(path=example_git_repo.path).ls()
2901
2936
[<GitRemoteCmd path=... remote_name=origin>]
2937
+
2938
+ For the example below, add a remote:
2939
+ >>> GitRemoteManager(path=example_git_repo.path).add(
2940
+ ... name='my_remote', url=f'file:///dev/null'
2941
+ ... )
2942
+ ''
2943
+
2944
+ >>> GitRemoteManager(path=example_git_repo.path).ls()
2945
+ [<GitRemoteCmd path=... remote_name=my_remote>,
2946
+ <GitRemoteCmd path=... remote_name=origin>]
2902
2947
"""
2903
- return QueryList (
2904
- [
2905
- GitRemoteCmd (path = self .path , remote_name = remote_name .lstrip ("* " ))
2906
- for remote_name in self ._ls ()
2907
- ],
2948
+ remote_str = self ._ls ()
2949
+ remote_pattern = re .compile (
2950
+ r"""
2951
+ (?P<name>\S+) # Remote name: one or more non-whitespace characters
2952
+ \s+ # One or more whitespace characters
2953
+ (?P<url>\S+) # URL: one or more non-whitespace characters
2954
+ \s+ # One or more whitespace characters
2955
+ \((?P<cmd_type>fetch|push)\) # 'fetch' or 'push' in parentheses
2956
+ """ ,
2957
+ re .VERBOSE | re .MULTILINE ,
2908
2958
)
2909
2959
2960
+ remotes : dict [str , dict [str , str | None ]] = {}
2961
+
2962
+ for match_obj in remote_pattern .finditer (remote_str ):
2963
+ name = match_obj .group ("name" )
2964
+ url = match_obj .group ("url" )
2965
+ cmd_type = match_obj .group ("cmd_type" )
2966
+
2967
+ if name not in remotes :
2968
+ remotes [name ] = {}
2969
+
2970
+ remotes [name ][cmd_type ] = url
2971
+
2972
+ remote_cmds : list [GitRemoteCmd ] = []
2973
+ for name , urls in remotes .items ():
2974
+ fetch_url = urls .get ("fetch" )
2975
+ push_url = urls .get ("push" )
2976
+ remote_cmds .append (
2977
+ GitRemoteCmd (
2978
+ path = self .path ,
2979
+ remote_name = name ,
2980
+ fetch_url = fetch_url ,
2981
+ push_url = push_url ,
2982
+ )
2983
+ )
2984
+
2985
+ return QueryList (remote_cmds )
2986
+
2910
2987
def get (self , * args : t .Any , ** kwargs : t .Any ) -> GitRemoteCmd | None :
2911
2988
"""Get remote via filter lookup.
2912
2989
0 commit comments