@@ -9,22 +9,20 @@ namespace GitCredentialManager;
99/// </summary>
1010/// <remarks>
1111/// <para>
12- /// A successful response holds the resolved <see cref="ICredential"/> together
13- /// with any non-credential output the provider wants to emit back to Git
14- /// (see <see cref="AdditionalProperties"/>).
12+ /// A response is exactly one of three shapes:
1513/// </para>
14+ /// <list type="bullet">
15+ /// <item><see cref="Ok(ICredential)"/> -- the provider produced a credential.</item>
16+ /// <item><see cref="Cancel"/> -- the provider declined and wants the whole
17+ /// credential acquisition pipeline to stop (emits <c>quit=1</c>; Git aborts
18+ /// the operation without a fallback interactive prompt).</item>
19+ /// <item><see cref="Yield"/> -- the provider has nothing to contribute but
20+ /// wants Git to continue trying other helpers or fall back to its built-in
21+ /// interactive prompt (emits an empty response).</item>
22+ /// </list>
1623/// <para>
17- /// A response can also be a <em>cancellation</em>: the provider was asked for a
18- /// credential but deliberately declined to produce one (the user closed an
19- /// auth prompt, no eligible account was found, and so on). A cancelled
20- /// response has no <see cref="Credential"/> and causes the command layer to
21- /// emit <c>quit=1</c> to Git, which terminates the credential acquisition
22- /// pipeline without falling back to an interactive prompt.
23- /// </para>
24- /// <para>
25- /// Use the <see cref="Ok(ICredential)"/> and <see cref="Cancel"/> factory
26- /// methods rather than the constructor where possible; they make the intent
27- /// of each call site obvious.
24+ /// Use the factory methods rather than the constructor where possible; they
25+ /// make the intent of each call site obvious.
2826/// </para>
2927/// <para>
3028/// Strongly-typed properties for newer Git credential protocol fields
@@ -38,44 +36,52 @@ namespace GitCredentialManager;
3836/// </remarks>
3937public class GitResponse
4038{
41- private GitResponse ( ICredential credential , bool isCancelled )
39+ private GitResponse ( ICredential credential , bool isCancelled , bool isYielded )
4240 {
43- if ( isCancelled && credential is not null )
41+ if ( isCancelled && isYielded )
4442 {
4543 throw new ArgumentException (
46- "A cancelled response cannot carry a credential." ,
44+ "A response cannot be both cancelled and yielded." ) ;
45+ }
46+
47+ if ( ( isCancelled || isYielded ) && credential is not null )
48+ {
49+ throw new ArgumentException (
50+ "A cancelled or yielded response cannot carry a credential." ,
4751 nameof ( credential ) ) ;
4852 }
4953
50- if ( ! isCancelled && credential is null )
54+ if ( ! isCancelled && ! isYielded && credential is null )
5155 {
5256 throw new ArgumentNullException (
5357 nameof ( credential ) ,
54- "A non-cancelled response must carry a credential. Use Cancel() instead." ) ;
58+ "A successful response must carry a credential. Use Cancel() or Yield () instead." ) ;
5559 }
5660
5761 Credential = credential ;
5862 IsCancelled = isCancelled ;
63+ IsYielded = isYielded ;
5964 }
6065
6166 /// <summary>
6267 /// Construct a successful response carrying the given credential.
6368 /// </summary>
6469 /// <remarks>Equivalent to <see cref="Ok(ICredential)"/>.</remarks>
6570 public GitResponse ( ICredential credential )
66- : this ( credential , isCancelled : false )
71+ : this ( credential , isCancelled : false , isYielded : false )
6772 {
6873 }
6974
7075 /// <summary>
7176 /// Construct a successful response carrying the given credential.
7277 /// </summary>
7378 public static GitResponse Ok ( ICredential credential ) =>
74- new GitResponse ( credential , isCancelled : false ) ;
79+ new GitResponse ( credential , isCancelled : false , isYielded : false ) ;
7580
7681 /// <summary>
7782 /// Construct a cancellation response: the provider declined to produce a
78- /// credential for this request.
83+ /// credential for this request, and the whole credential acquisition
84+ /// pipeline should stop.
7985 /// </summary>
8086 /// <remarks>
8187 /// The command layer translates a cancelled response into a <c>quit=1</c>
@@ -85,28 +91,52 @@ public static GitResponse Ok(ICredential credential) =>
8591 /// response are ignored.
8692 /// </remarks>
8793 public static GitResponse Cancel ( ) =>
88- new GitResponse ( credential : null , isCancelled : true ) ;
94+ new GitResponse ( credential : null , isCancelled : true , isYielded : false ) ;
95+
96+ /// <summary>
97+ /// Construct a yielded response: the provider has nothing to contribute
98+ /// for this request but does not want to stop other helpers from being
99+ /// tried (or Git's interactive prompt from being shown).
100+ /// </summary>
101+ /// <remarks>
102+ /// The command layer translates a yielded response into an empty response
103+ /// on standard output (no credential fields, no <c>quit</c> signal). Git
104+ /// then proceeds to the next helper in the chain or to its built-in
105+ /// interactive prompt. Any <see cref="AdditionalProperties"/> set on a
106+ /// yielded response are ignored.
107+ /// </remarks>
108+ public static GitResponse Yield ( ) =>
109+ new GitResponse ( credential : null , isCancelled : false , isYielded : true ) ;
89110
90111 /// <summary>
91112 /// The credential resolved or generated for the request, or <see langword="null"/>
92- /// when <see cref="IsCancelled"/> is <see langword="true"/>.
113+ /// when <see cref="IsCancelled"/> or <see cref="IsYielded"/> is <see langword="true"/>.
93114 /// </summary>
94115 public ICredential Credential { get ; }
95116
96117 /// <summary>
97118 /// <see langword="true"/> when the provider declined to produce a credential
98- /// for this request. The command layer translates this into a <c>quit=1</c>
99- /// signal to Git rather than any specific exit code.
119+ /// for this request and wants the whole credential acquisition pipeline to
120+ /// stop. The command layer translates this into a <c>quit=1</c> signal to
121+ /// Git.
100122 /// </summary>
101123 public bool IsCancelled { get ; }
102124
125+ /// <summary>
126+ /// <see langword="true"/> when the provider has nothing to contribute but
127+ /// does not want to stop the credential acquisition pipeline. The command
128+ /// layer translates this into an empty response on standard output so Git
129+ /// proceeds to the next helper or its interactive prompt.
130+ /// </summary>
131+ public bool IsYielded { get ; }
132+
103133 /// <summary>
104134 /// Additional, untyped output to be emitted alongside the credential.
105135 /// </summary>
106136 /// <remarks>
107137 /// This is the legacy escape hatch for non-credential output. New code
108138 /// should prefer typed properties on <see cref="GitResponse"/> as they
109- /// are added. Ignored on cancelled responses.
139+ /// are added. Ignored on cancelled and yielded responses.
110140 /// </remarks>
111141 public IDictionary < string , string > AdditionalProperties { get ; set ; }
112142 = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase ) ;
0 commit comments