@@ -36,32 +36,76 @@ def get_repo_creds_and_default_branch(
3636
3737 # no auth
3838 with suppress (InvalidRepoCredentialsError ):
39- return _get_repo_creds_and_default_branch_https (url )
39+ creds , default_branch = _get_repo_creds_and_default_branch_https (url )
40+ logger .debug (
41+ "Git repo %s is public. Using no auth. Default branch: %s" , repo_url , default_branch
42+ )
43+ return creds , default_branch
4044
4145 # ssh key provided by the user or pulled from the server
4246 if identity_file is not None or private_key is not None :
4347 if identity_file is not None :
4448 private_key = _read_private_key (identity_file )
45- return _get_repo_creds_and_default_branch_ssh (url , identity_file , private_key )
49+ creds , default_branch = _get_repo_creds_and_default_branch_ssh (
50+ url , identity_file , private_key
51+ )
52+ logger .debug (
53+ "Git repo %s is private. Using identity file: %s. Default branch: %s" ,
54+ repo_url ,
55+ identity_file ,
56+ default_branch ,
57+ )
58+ return creds , default_branch
4659 elif private_key is not None :
4760 with NamedTemporaryFile ("w+" , 0o600 ) as f :
4861 f .write (private_key )
4962 f .flush ()
50- return _get_repo_creds_and_default_branch_ssh (url , f .name , private_key )
63+ creds , default_branch = _get_repo_creds_and_default_branch_ssh (
64+ url , f .name , private_key
65+ )
66+ masked_key = "***" + private_key [- 10 :] if len (private_key ) > 10 else "***MASKED***"
67+ logger .debug (
68+ "Git repo %s is private. Using private key: %s. Default branch: %s" ,
69+ repo_url ,
70+ masked_key ,
71+ default_branch ,
72+ )
73+ return creds , default_branch
5174 else :
5275 assert False , "should not reach here"
5376
5477 # oauth token provided by the user or pulled from the server
5578 if oauth_token is not None :
56- return _get_repo_creds_and_default_branch_https (url , oauth_token )
79+ creds , default_branch = _get_repo_creds_and_default_branch_https (url , oauth_token )
80+ masked_token = (
81+ len (oauth_token [:- 4 ]) * "*" + oauth_token [- 4 :]
82+ if len (oauth_token ) > 4
83+ else "***MASKED***"
84+ )
85+ logger .debug (
86+ "Git repo %s is private. Using provided OAuth token: %s. Default branch: %s" ,
87+ repo_url ,
88+ masked_token ,
89+ default_branch ,
90+ )
91+ return creds , default_branch
5792
5893 # key from ssh config
5994 identities = get_host_config (url .original_host ).get ("identityfile" )
6095 if identities :
6196 _identity_file = identities [0 ]
6297 with suppress (InvalidRepoCredentialsError ):
6398 _private_key = _read_private_key (_identity_file )
64- return _get_repo_creds_and_default_branch_ssh (url , _identity_file , _private_key )
99+ creds , default_branch = _get_repo_creds_and_default_branch_ssh (
100+ url , _identity_file , _private_key
101+ )
102+ logger .debug (
103+ "Git repo %s is private. Using SSH config identity file: %s. Default branch: %s" ,
104+ repo_url ,
105+ _identity_file ,
106+ default_branch ,
107+ )
108+ return creds , default_branch
65109
66110 # token from gh config
67111 if os .path .exists (gh_config_path ):
@@ -70,13 +114,35 @@ def get_repo_creds_and_default_branch(
70114 _oauth_token = gh_hosts .get (url .host , {}).get ("oauth_token" )
71115 if _oauth_token is not None :
72116 with suppress (InvalidRepoCredentialsError ):
73- return _get_repo_creds_and_default_branch_https (url , _oauth_token )
117+ creds , default_branch = _get_repo_creds_and_default_branch_https (url , _oauth_token )
118+ masked_token = (
119+ len (_oauth_token [:- 4 ]) * "*" + _oauth_token [- 4 :]
120+ if len (_oauth_token ) > 4
121+ else "***MASKED***"
122+ )
123+ logger .debug (
124+ "Git repo %s is private. Using GitHub config token: %s from %s. Default branch: %s" ,
125+ repo_url ,
126+ masked_token ,
127+ gh_config_path ,
128+ default_branch ,
129+ )
130+ return creds , default_branch
74131
75132 # default user key
76133 if os .path .exists (default_ssh_key ):
77134 with suppress (InvalidRepoCredentialsError ):
78135 _private_key = _read_private_key (default_ssh_key )
79- return _get_repo_creds_and_default_branch_ssh (url , default_ssh_key , _private_key )
136+ creds , default_branch = _get_repo_creds_and_default_branch_ssh (
137+ url , default_ssh_key , _private_key
138+ )
139+ logger .debug (
140+ "Git repo %s is private. Using default identity file: %s. Default branch: %s" ,
141+ repo_url ,
142+ default_ssh_key ,
143+ default_branch ,
144+ )
145+ return creds , default_branch
80146
81147 raise InvalidRepoCredentialsError (
82148 "No valid default Git credentials found. Pass valid `--token` or `--git-identity`."
@@ -87,8 +153,9 @@ def _get_repo_creds_and_default_branch_ssh(
87153 url : GitRepoURL , identity_file : PathLike , private_key : str
88154) -> tuple [RemoteRepoCreds , Optional [str ]]:
89155 _url = url .as_ssh ()
156+ env = _make_git_env_for_creds_check (identity_file = identity_file )
90157 try :
91- default_branch = _get_repo_default_branch (_url , make_git_env ( identity_file = identity_file ) )
158+ default_branch = _get_repo_default_branch (_url , env )
92159 except GitCommandError as e :
93160 message = f"Cannot access `{ _url } ` using the `{ identity_file } ` private SSH key"
94161 raise InvalidRepoCredentialsError (message ) from e
@@ -104,8 +171,9 @@ def _get_repo_creds_and_default_branch_https(
104171 url : GitRepoURL , oauth_token : Optional [str ] = None
105172) -> tuple [RemoteRepoCreds , Optional [str ]]:
106173 _url = url .as_https ()
174+ env = _make_git_env_for_creds_check ()
107175 try :
108- default_branch = _get_repo_default_branch (url .as_https (oauth_token ), make_git_env () )
176+ default_branch = _get_repo_default_branch (url .as_https (oauth_token ), env )
109177 except GitCommandError as e :
110178 message = f"Cannot access `{ _url } `"
111179 if oauth_token is not None :
@@ -120,10 +188,32 @@ def _get_repo_creds_and_default_branch_https(
120188 return creds , default_branch
121189
122190
191+ def _make_git_env_for_creds_check (identity_file : Optional [PathLike ] = None ) -> dict [str , str ]:
192+ # Our goal is to check if _provided_ creds (if any) are correct, so we need to be sure that
193+ # only the provided creds are used, without falling back to any additional mechanisms.
194+ # To do this, we:
195+ # 1. Disable all configs to ignore any stored creds
196+ # 2. Disable askpass to avoid asking for creds interactively or fetching stored creds from
197+ # a non-interactive askpass helper (for example, VS Code sets GIT_ASKPASS to its own helper,
198+ # which silently provides creds to Git).
199+ return make_git_env (disable_config = True , disable_askpass = True , identity_file = identity_file )
200+
201+
123202def _get_repo_default_branch (url : str , env : dict [str , str ]) -> Optional [str ]:
203+ # Git shipped by Apple with XCode is patched to support an additional config scope
204+ # above "system" called "xcode". There is no option in `git config list` to show this config,
205+ # but you can list the merged config (`git config list` without options) and then exclude
206+ # all settings listed in `git config list --{system,global,local,worktree}`.
207+ # As of time of writing, there are only two settings in the "xcode" config, one of which breaks
208+ # our "is repo public?" check, namely "credential.helper=osxkeychain".
209+ # As there is no way to disable "xcode" config (no env variable, no CLI option, etc.),
210+ # the only way to disable credential helper is to override this specific setting with an empty
211+ # string via command line argument: `git -c credential.helper= COMMAND [ARGS ...]`.
212+ # See: https://github.com/git/git/commit/3d4355712b9fe77a96ad4ad877d92dc7ff6e0874
213+ # See: https://gist.github.com/ChrisTollefson/ab9c0a5d1dd4dd615217345c6936a307
214+ _git = git .cmd .Git ()(c = "credential.helper=" )
124215 # output example: "ref: refs/heads/dev\tHEAD\n545344f77c0df78367085952a97fc3a058eb4c65\tHEAD"
125- # Disable credential helpers to exclude any default credentials from being used
126- output : str = git .cmd .Git ()(c = "credential.helper=" ).ls_remote ("--symref" , url , "HEAD" , env = env )
216+ output : str = _git .ls_remote ("--symref" , url , "HEAD" , env = env )
127217 for line in output .splitlines ():
128218 # line format: `<oid> TAB <ref> LF`
129219 oid , _ , ref = line .partition ("\t " )
0 commit comments