1414using System . Reactive ;
1515using System . Collections . Generic ;
1616using LibGit2Sharp ;
17+ using PullRequest = Octokit . PullRequest ;
1718
1819namespace GitHub . Services
1920{
@@ -83,14 +84,33 @@ public IObservable<string> GetPullRequestTemplate(ILocalRepositoryModel reposito
8384 } ) ;
8485 }
8586
87+ public IObservable < bool > CleanForCheckout ( ILocalRepositoryModel repository )
88+ {
89+ var repo = gitService . GetRepository ( repository . LocalPath ) ;
90+ return Observable . Return ( ! repo . RetrieveStatus ( ) . IsDirty ) ;
91+ }
92+
8693 public IObservable < Unit > FetchAndCheckout ( ILocalRepositoryModel repository , int pullRequestNumber , string localBranchName )
8794 {
8895 return DoFetchAndCheckout ( repository , pullRequestNumber , localBranchName ) . ToObservable ( ) ;
8996 }
9097
91- public string GetDefaultLocalBranchName ( int pullRequestNumber , string pullRequestTitle )
98+ public IObservable < string > GetDefaultLocalBranchName ( ILocalRepositoryModel repository , int pullRequestNumber , string pullRequestTitle )
9299 {
93- return "pr/" + pullRequestNumber + "-" + GetSafeBranchName ( pullRequestTitle ) ;
100+ return Observable . Defer ( ( ) =>
101+ {
102+ var initial = "pr/" + pullRequestNumber + "-" + GetSafeBranchName ( pullRequestTitle ) ;
103+ var current = initial ;
104+ var repo = gitService . GetRepository ( repository . LocalPath ) ;
105+ var index = 2 ;
106+
107+ while ( repo . Branches [ current ] != null )
108+ {
109+ current = initial + '-' + index ++ ;
110+ }
111+
112+ return Observable . Return ( current ) ;
113+ } ) ;
94114 }
95115
96116 public IObservable < HistoryDivergence > CalculateHistoryDivergence ( ILocalRepositoryModel repository , int pullRequestNumber )
@@ -117,43 +137,92 @@ public IObservable<HistoryDivergence> CalculateHistoryDivergence(ILocalRepositor
117137 } ) ;
118138 }
119139
120- public IObservable < IBranch > GetLocalBranches ( ILocalRepositoryModel repository , int number )
140+ public IObservable < IBranch > GetLocalBranches ( ILocalRepositoryModel repository , PullRequest pullRequest )
121141 {
122142 return Observable . Defer ( ( ) =>
123143 {
124144 var repo = gitService . GetRepository ( repository . LocalPath ) ;
125- var result = GetLocalBranchesInternal ( repo , number ) . Select ( x => new BranchModel ( x , repository ) ) ;
145+ var result = GetLocalBranchesInternal ( repository , repo , pullRequest ) . Select ( x => new BranchModel ( x , repository ) ) ;
126146 return result . ToObservable ( ) ;
127147 } ) ;
128148 }
129149
130- public IObservable < Unit > SwitchToBranch ( ILocalRepositoryModel repository , int number )
150+ public bool IsPullRequestFromFork ( ILocalRepositoryModel repository , PullRequest pullRequest )
131151 {
132- return Observable . Defer ( ( ) =>
152+ var sourceUrl = new UriString ( pullRequest . Head . Repository . CloneUrl ) ;
153+ return sourceUrl . ToRepositoryUrl ( ) != repository . CloneUrl . ToRepositoryUrl ( ) ;
154+ }
155+
156+ public IObservable < Unit > SwitchToBranch ( ILocalRepositoryModel repository , PullRequest pullRequest )
157+ {
158+ return Observable . Defer ( async ( ) =>
133159 {
134160 var repo = gitService . GetRepository ( repository . LocalPath ) ;
135- var branch = GetLocalBranchesInternal ( repo , number ) . First ( ) ;
136- gitClient . Checkout ( repo , branch ) ;
161+ var branchName = GetLocalBranchesInternal ( repository , repo , pullRequest ) . First ( ) ;
162+
163+ await gitClient . Fetch ( repo , "origin" ) ;
164+
165+ var branch = repo . Branches [ branchName ] ;
166+
167+ if ( branch == null )
168+ {
169+ var trackedBranchName = $ "refs/remotes/origin/" + branchName ;
170+ var trackedBranch = repo . Branches [ trackedBranchName ] ;
171+
172+ if ( trackedBranch != null )
173+ {
174+ branch = repo . CreateBranch ( branchName , trackedBranch . Tip ) ;
175+ await gitClient . SetTrackingBranch ( repo , branchName , trackedBranchName ) ;
176+ }
177+ else
178+ {
179+ throw new InvalidOperationException ( $ "Could not find branch '{ trackedBranchName } '.") ;
180+ }
181+ }
182+
183+ await gitClient . Checkout ( repo , branchName ) ;
184+
137185 return Observable . Empty < Unit > ( ) ;
138186 } ) ;
139187 }
140188
189+ public IObservable < Unit > UnmarkLocalBranch ( ILocalRepositoryModel repository )
190+ {
191+ return Observable . Defer ( async ( ) =>
192+ {
193+ var repo = gitService . GetRepository ( repository . LocalPath ) ;
194+ var configKey = $ "branch.{ repo . Head . FriendlyName } .ghfvs-pr";
195+ await gitClient . UnsetConfig ( repo , configKey ) ;
196+ return Observable . Return ( Unit . Default ) ;
197+ } ) ;
198+ }
199+
141200 async Task DoFetchAndCheckout ( ILocalRepositoryModel repository , int pullRequestNumber , string localBranchName )
142201 {
143202 var repo = gitService . GetRepository ( repository . LocalPath ) ;
144- var configKey = $ "branch.{ BranchNameToConfigKey ( localBranchName ) } .ghfvs-pr";
203+ var configKey = $ "branch.{ localBranchName } .ghfvs-pr";
145204 await gitClient . Fetch ( repo , "origin" , new [ ] { $ "refs/pull/{ pullRequestNumber } /head:{ localBranchName } " } ) ;
146205 await gitClient . Checkout ( repo , localBranchName ) ;
147206 await gitClient . SetConfig ( repo , configKey , pullRequestNumber . ToString ( ) ) ;
148207 }
149208
150- IEnumerable < string > GetLocalBranchesInternal ( IRepository repository , int number )
209+ IEnumerable < string > GetLocalBranchesInternal (
210+ ILocalRepositoryModel localRepository ,
211+ IRepository repository ,
212+ PullRequest pullRequest )
151213 {
152- var pr = number . ToString ( ) ;
153- return repository . Config
154- . Select ( x => new { Branch = BranchCapture . Match ( x . Key ) . Groups [ "branch" ] . Value , Value = x . Value } )
155- . Where ( x => ! string . IsNullOrWhiteSpace ( x . Branch ) && x . Value == pr )
156- . Select ( x => ConfigKeyToBranchName ( x . Branch ) ) ;
214+ if ( ! IsPullRequestFromFork ( localRepository , pullRequest ) )
215+ {
216+ return new [ ] { pullRequest . Head . Ref } ;
217+ }
218+ else
219+ {
220+ var pr = pullRequest . Number . ToString ( CultureInfo . InvariantCulture ) ;
221+ return repository . Config
222+ . Select ( x => new { Branch = BranchCapture . Match ( x . Key ) . Groups [ "branch" ] . Value , Value = x . Value } )
223+ . Where ( x => ! string . IsNullOrWhiteSpace ( x . Branch ) && x . Value == pr )
224+ . Select ( x => x . Branch ) ;
225+ }
157226 }
158227
159228 async Task < IPullRequestModel > PushAndCreatePR ( IRepositoryHost host ,
@@ -191,23 +260,19 @@ void EnsurePullRefSpecExists(IRepository repo)
191260
192261 static string GetSafeBranchName ( string name )
193262 {
194- var before = InvalidBranchCharsRegex . Replace ( name , "-" ) ;
263+ var before = InvalidBranchCharsRegex . Replace ( name , "-" ) . TrimEnd ( '-' ) ;
195264
196265 for ( ; ; )
197266 {
198267 string after = before . Replace ( "--" , "-" ) ;
199268
200269 if ( after == before )
201270 {
202- return before . ToLower ( CultureInfo . InvariantCulture ) ;
271+ return before . ToLower ( CultureInfo . CurrentCulture ) ;
203272 }
204273
205274 before = after ;
206275 }
207276 }
208-
209- static string BranchNameToConfigKey ( string name ) => name . Replace ( "/" , "." ) ;
210-
211- static string ConfigKeyToBranchName ( string name ) => name . Replace ( "." , "/" ) ;
212277 }
213278}
0 commit comments