public IObservable <Unit> Checkout(ILocalRepositoryModel repository, IPullRequestModel pullRequest, string localBranchName) { return(Observable.Defer(async() => { var repo = gitService.GetRepository(repository.LocalPath); var existing = repo.Branches[localBranchName]; if (existing != null) { await gitClient.Checkout(repo, localBranchName); } else if (repository.CloneUrl.ToRepositoryUrl() == pullRequest.Head.RepositoryCloneUrl.ToRepositoryUrl()) { await gitClient.Fetch(repo, "origin"); await gitClient.Checkout(repo, localBranchName); } else { var refSpec = $"{pullRequest.Head.Ref}:{localBranchName}"; var prConfigKey = $"branch.{localBranchName}.ghfvs-pr"; var remoteName = pullRequest.Head.RepositoryCloneUrl.Owner; var remoteUri = pullRequest.Head.RepositoryCloneUrl; await gitClient.SetRemote(repo, remoteName, new Uri(remoteUri)); await gitClient.Fetch(repo, remoteName); await gitClient.Fetch(repo, remoteName, new[] { refSpec }); await gitClient.Checkout(repo, localBranchName); await gitClient.SetTrackingBranch(repo, localBranchName, $"refs/remotes/{remoteName}/{pullRequest.Head.Ref}"); await gitClient.SetConfig(repo, prConfigKey, pullRequest.Number.ToString()); } return Observable.Return(Unit.Default); })); }
async Task <IPullRequestDirectoryNode> CreateChangedFilesTree(IPullRequestModel pullRequest, TreeChanges changes) { var dirs = new Dictionary <string, PullRequestDirectoryNode> { { string.Empty, new PullRequestDirectoryNode(string.Empty) } }; foreach (var changedFile in pullRequest.ChangedFiles) { var node = new PullRequestFileNode( LocalRepository.LocalPath, changedFile.FileName, changedFile.Sha, changedFile.Status, GetOldFileName(changedFile, changes)); var file = await Session.GetFile(changedFile.FileName); var fileCommentCount = file?.WhenAnyValue(x => x.InlineCommentThreads) .Subscribe(x => node.CommentCount = x.Count(y => y.LineNumber != -1)); var dir = GetDirectory(node.DirectoryPath, dirs); dir.Files.Add(node); } return(dirs[string.Empty]); }
async Task Load(IPullRequestModel pullRequest) { try { session = await sessionManager.GetSession(pullRequest); PullRequestModel = pullRequest; Model = pullRequest.Reviews.FirstOrDefault(x => x.State == PullRequestReviewState.Pending && x.User.Login == session.User.Login) ?? new PullRequestReviewModel { Body = string.Empty, User = session.User, State = PullRequestReviewState.Pending, }; Body = Model.Body; sessionSubscription?.Dispose(); await UpdateFileComments(); sessionSubscription = session.PullRequestChanged.Subscribe(_ => UpdateFileComments().Forget()); } finally { IsBusy = false; } }
public IObservable <Tuple <string, string> > ExtractDiffFiles( ILocalRepositoryModel repository, IModelService modelService, IPullRequestModel pullRequest, string fileName, string fileSha) { return(Observable.Defer(async() => { var repo = gitService.GetRepository(repository.LocalPath); var remote = await gitClient.GetHttpRemote(repo, "origin"); await gitClient.Fetch(repo, remote.Name); // The left file is the target of the PR so this should already be fetched. var left = await gitClient.ExtractFile(repo, pullRequest.Base.Sha, fileName); // The right file - if it comes from a fork - may not be fetched so fall back to // getting the file contents from the model service. var right = await GetFileFromRepositoryOrApi(repository, repo, modelService, pullRequest.Head.Sha, fileName, fileSha); if (left == null) { throw new FileNotFoundException($"Could not retrieve {fileName}@{pullRequest.Base.Sha}"); } if (right == null) { throw new FileNotFoundException($"Could not retrieve {fileName}@{pullRequest.Head.Sha}"); } return Observable.Return(Tuple.Create(left, right)); })); }
public IObservable <Unit> Checkout(ILocalRepositoryModel repository, IPullRequestModel pullRequest, string localBranchName) { return(Observable.Defer(async() => { var repo = gitService.GetRepository(repository.LocalPath); var existing = repo.Branches[localBranchName]; if (existing != null) { await gitClient.Checkout(repo, localBranchName); } else if (repository.CloneUrl.ToRepositoryUrl() == pullRequest.Head.RepositoryCloneUrl.ToRepositoryUrl()) { var remote = await gitClient.GetHttpRemote(repo, "origin"); await gitClient.Fetch(repo, remote.Name); await gitClient.Checkout(repo, localBranchName); } else { var refSpec = $"{pullRequest.Head.Ref}:{localBranchName}"; var remoteName = await CreateRemote(repo, pullRequest.Head.RepositoryCloneUrl); await gitClient.Fetch(repo, remoteName); await gitClient.Fetch(repo, remoteName, new[] { refSpec }); await gitClient.Checkout(repo, localBranchName); await gitClient.SetTrackingBranch(repo, localBranchName, $"refs/remotes/{remoteName}/{pullRequest.Head.Ref}"); } // Store the PR number in the branch config with the key "ghfvs-pr". var prConfigKey = $"branch.{localBranchName}.{SettingGHfVSPullRequest}"; await gitClient.SetConfig(repo, prConfigKey, BuildGHfVSConfigKeyValue(pullRequest)); return Observable.Return(Unit.Default); })); }
/// <inheritdoc/> public IReadOnlyList <IInlineCommentThreadModel> BuildCommentThreads( IPullRequestModel pullRequest, string relativePath, IReadOnlyList <DiffChunk> diff) { relativePath = relativePath.Replace("\\", "/"); var commentsByPosition = pullRequest.ReviewComments .Where(x => x.Path == relativePath && x.OriginalPosition.HasValue) .OrderBy(x => x.Id) .GroupBy(x => Tuple.Create(x.OriginalCommitId, x.OriginalPosition.Value)); var threads = new List <IInlineCommentThreadModel>(); foreach (var comments in commentsByPosition) { var hunk = comments.First().DiffHunk; var chunks = DiffUtilities.ParseFragment(hunk); var chunk = chunks.Last(); var diffLines = chunk.Lines.Reverse().Take(5).ToList(); var thread = new InlineCommentThreadModel( relativePath, comments.Key.Item1, comments.Key.Item2, diffLines, comments); threads.Add(thread); } UpdateCommentThreads(threads, diff); return(threads); }
async Task <PullRequestSession> GetSessionInternal(IPullRequestModel pullRequest) { PullRequestSession session = null; WeakReference <PullRequestSession> weakSession; var key = Tuple.Create(pullRequest.Base.RepositoryCloneUrl.Owner, pullRequest.Number); if (sessions.TryGetValue(key, out weakSession)) { weakSession.TryGetTarget(out session); } if (session == null) { var modelService = hosts.LookupHost(HostAddress.Create(repository.CloneUrl))?.ModelService; if (modelService != null) { session = new PullRequestSession( sessionService, await modelService.GetCurrentUser(), pullRequest, repository, key.Item1, false); sessions[key] = new WeakReference <PullRequestSession>(session); } } else { await session.Update(pullRequest); } return(session); }
/// <inheritdoc/> async Task Load(IAccount author, IPullRequestModel pullRequest) { IsBusy = true; try { session = await sessionManager.GetSession(pullRequest); User = author; PullRequestTitle = pullRequest.Title; var reviews = new List <IPullRequestReviewViewModel>(); var isFirst = true; foreach (var review in pullRequest.Reviews.OrderByDescending(x => x.SubmittedAt)) { if (review.User.Login == author.Login && review.State != PullRequestReviewState.Pending) { var vm = new PullRequestReviewViewModel(editorService, session, pullRequest, review); vm.IsExpanded = isFirst; reviews.Add(vm); isFirst = false; } } Reviews = reviews; } finally { IsBusy = false; } }
static PullRequestReviewAuthoringViewModel CreateTarget( IPullRequestModel model, IPullRequestSession session = null) { return(CreateTarget( sessionManager: CreateSessionManager(session), modelServiceFactory: CreateModelServiceFactory(CreateModelService(model)))); }
PullRequestStatusViewModel CreatePullRequestStatusViewModel(IPullRequestModel pullRequest) { var pullRequestStatusViewModel = new PullRequestStatusViewModel(showCurrentPullRequestCommand); pullRequestStatusViewModel.Number = pullRequest.Number; pullRequestStatusViewModel.Title = pullRequest.Title; return(pullRequestStatusViewModel); }
bool IEquatable <IPullRequestModel> .Equals([AllowNull] IPullRequestModel other) { if (ReferenceEquals(this, other)) { return(true); } return(other != null && Number == other.Number); }
public bool IsPullRequestFromRepository(ILocalRepositoryModel repository, IPullRequestModel pullRequest) { if (pullRequest.Head?.RepositoryCloneUrl != null) { return(repository.CloneUrl?.ToRepositoryUrl() == pullRequest.Head.RepositoryCloneUrl.ToRepositoryUrl()); } return(false); }
public async Task Update(IPullRequestModel pullRequest) { PullRequest = pullRequest; foreach (var file in this.fileIndex.Values.ToList()) { await UpdateFile(file); } }
static IModelService CreateModelService(IPullRequestModel pullRequest = null) { pullRequest = pullRequest ?? CreatePullRequest(); var result = Substitute.For <IModelService>(); result.GetPullRequest(null, null, 0).ReturnsForAnyArgs(Observable.Return(pullRequest)); return(result); }
PullRequestStatusViewModel CreatePullRequestStatusViewModel(IPullRequestModel pullRequest) { var dte = serviceProvider.TryGetService <EnvDTE.DTE>(); var command = new RaisePullRequestCommand(dte, usageTracker); var pullRequestStatusViewModel = new PullRequestStatusViewModel(command); pullRequestStatusViewModel.Number = pullRequest.Number; pullRequestStatusViewModel.Title = pullRequest.Title; return(pullRequestStatusViewModel); }
public bool IsPullRequestFromFork(ILocalRepositoryModel repository, IPullRequestModel pullRequest) { if (pullRequest.Head?.Label != null && pullRequest.Base?.Label != null) { var headOwner = pullRequest.Head.Label.Split(':')[0]; var baseOwner = pullRequest.Base.Label.Split(':')[0]; return(headOwner != baseOwner); } return(false); }
public void CopyFrom(IPullRequestModel other) { if (!Equals(other)) { throw new ArgumentException("Instance to copy from doesn't match this instance. this:(" + this + ") other:(" + other + ")", nameof(other)); } Title = other.Title; UpdatedAt = other.UpdatedAt; CommentCount = other.CommentCount; HasNewComments = other.HasNewComments; }
/// <inheritdoc/> public async Task <IPullRequestSession> GetSession(IPullRequestModel pullRequest) { if (await service.EnsureLocalBranchesAreMarkedAsPullRequests(repository, pullRequest)) { // The branch for the PR was not previously marked with the PR number in the git // config so we didn't pick up that the current branch is a PR branch. That has // now been corrected, so call RepoChanged to make sure everything is up-to-date. await RepoChanged(repository); } return(await GetSessionInternal(pullRequest)); }
/// <summary> /// Loads the view model from octokit models. /// </summary> /// <param name="pullRequest">The pull request model.</param> /// <param name="files">The pull request's changed files.</param> public async Task Load(IPullRequestModel pullRequest) { Model = pullRequest; Title = Resources.PullRequestNavigationItemText + " #" + pullRequest.Number; SourceBranchDisplayName = GetBranchDisplayName(pullRequest.Head?.Label); TargetBranchDisplayName = GetBranchDisplayName(pullRequest.Base.Label); Body = !string.IsNullOrWhiteSpace(pullRequest.Body) ? pullRequest.Body : "*No description provided.*"; ChangedFilesTree.Clear(); ChangedFilesList.Clear(); // WPF doesn't support AddRange here so iterate through the changes. foreach (var change in CreateChangedFilesList(pullRequest.ChangedFiles)) { ChangedFilesList.Add(change); } foreach (var change in CreateChangedFilesTree(ChangedFilesList).Children) { ChangedFilesTree.Add(change); } var localBranches = await pullRequestsService.GetLocalBranches(repository, pullRequest).ToList(); var isCheckedOut = localBranches.Contains(repository.CurrentBranch); if (isCheckedOut) { var divergence = await pullRequestsService.CalculateHistoryDivergence(repository, Model.Number); var pullDisabled = divergence.BehindBy == 0 ? "No commits to pull" : null; var pushDisabled = divergence.AheadBy == 0 ? "No commits to push" : divergence.BehindBy > 0 ? "You must pull before you can push" : null; UpdateState = new UpdateCommandState(divergence, pullDisabled, pushDisabled); CheckoutState = null; } else { var caption = localBranches.Count > 0 ? "Checkout " + localBranches.First().DisplayName : "Checkout to " + (await pullRequestsService.GetDefaultLocalBranchName(repository, Model.Number, Model.Title)); var disabled = await pullRequestsService.IsWorkingDirectoryClean(repository) ? null : "Cannot checkout as your working directory has uncommitted changes."; CheckoutState = new CheckoutCommandState(caption, disabled); UpdateState = null; } IsBusy = false; }
public async Task Update(IPullRequestModel pullRequest) { PullRequest = pullRequest; mergeBase = null; foreach (var file in this.fileIndex.Values.ToList()) { await UpdateFile(file); } pullRequestChanged.OnNext(pullRequest); }
public async Task <string> GetMergeBase(ILocalRepositoryModel repository, IPullRequestModel pullRequest) { using (var repo = gitService.GetRepository(repository.LocalPath)) { return(await gitClient.GetPullRequestMergeBase( repo, pullRequest.Base.RepositoryCloneUrl, pullRequest.Base.Sha, pullRequest.Head.Sha, pullRequest.Base.Ref, pullRequest.Number)); } }
/// <summary> /// Builds a collection of <see cref="PullRequestReviewSummaryViewModel"/>s by user. /// </summary> /// <param name="currentUser">The current user.</param> /// <param name="pullRequest">The pull request model.</param> /// <remarks> /// This method builds a list similar to that found in the "Reviewers" section at the top- /// right of the Pull Request page on GitHub. /// </remarks> public static IEnumerable <PullRequestReviewSummaryViewModel> BuildByUser( IAccount currentUser, IPullRequestModel pullRequest) { var existing = new Dictionary <string, PullRequestReviewSummaryViewModel>(); foreach (var review in pullRequest.Reviews.OrderBy(x => x.Id)) { if (review.State == PullRequestReviewState.Pending && review.User.Login != currentUser.Login) { continue; } PullRequestReviewSummaryViewModel previous; existing.TryGetValue(review.User.Login, out previous); var previousPriority = ToPriority(previous); var reviewPriority = ToPriority(review.State); if (reviewPriority >= previousPriority) { var count = pullRequest.ReviewComments .Where(x => x.PullRequestReviewId == review.Id) .Count(); existing[review.User.Login] = new PullRequestReviewSummaryViewModel { Id = review.Id, User = review.User, State = review.State, FileCommentCount = count }; } } var result = existing.Values.OrderBy(x => x.User).AsEnumerable(); if (!result.Any(x => x.State == PullRequestReviewState.Pending)) { var newReview = new PullRequestReviewSummaryViewModel { State = PullRequestReviewState.Pending, User = currentUser, }; result = result.Concat(new[] { newReview }); } return(result); }
PullRequestReviewViewModel CreateTarget( IPullRequestEditorService editorService = null, IPullRequestSession session = null, IPullRequestModel pullRequest = null, IPullRequestReviewModel model = null) { editorService = editorService ?? Substitute.For <IPullRequestEditorService>(); session = session ?? Substitute.For <IPullRequestSession>(); pullRequest = pullRequest ?? CreatePullRequest(); model = model ?? pullRequest.Reviews[0]; return(new PullRequestReviewViewModel( editorService, session, pullRequest, model)); }
IModelServiceFactory CreateModelServiceFactory(IPullRequestModel pullRequest = null) { var modelService = Substitute.For <IModelService>(); modelService.GetPullRequest(null, null, 0).ReturnsForAnyArgs(x => { var cloneUrl = $"https://github.com/{x.ArgAt<string>(0)}/{x.ArgAt<string>(1)}"; var pr = pullRequest ?? CreatePullRequestModel(x.ArgAt <int>(2), cloneUrl); return(Observable.Return(pr)); }); var factory = Substitute.For <IModelServiceFactory>(); factory.CreateAsync(null).ReturnsForAnyArgs(modelService); factory.CreateBlocking(null).ReturnsForAnyArgs(modelService); return(factory); }
IEnumerable <string> GetLocalBranchesInternal( ILocalRepositoryModel localRepository, IRepository repository, IPullRequestModel pullRequest) { if (!IsPullRequestFromFork(localRepository, pullRequest)) { return(new[] { pullRequest.Head.Ref }); } else { var pr = pullRequest.Number.ToString(CultureInfo.InvariantCulture); return(repository.Config .Select(x => new { Branch = BranchCapture.Match(x.Key).Groups["branch"].Value, Value = x.Value }) .Where(x => !string.IsNullOrWhiteSpace(x.Branch) && x.Value == pr) .Select(x => x.Branch)); } }
public PullRequestSession( IPullRequestSessionService service, IAccount user, IPullRequestModel pullRequest, ILocalRepositoryModel repository, bool isCheckedOut) { Guard.ArgumentNotNull(service, nameof(service)); Guard.ArgumentNotNull(user, nameof(user)); Guard.ArgumentNotNull(pullRequest, nameof(pullRequest)); Guard.ArgumentNotNull(repository, nameof(repository)); this.service = service; this.isCheckedOut = isCheckedOut; User = user; PullRequest = pullRequest; Repository = repository; }
/// <summary> /// Initializes a new instance of the <see cref="PullRequestReviewViewModel"/> class. /// </summary> /// <param name="editorService">The pull request editor service.</param> /// <param name="session">The pull request session.</param> /// <param name="pullRequest">The pull request model.</param> /// <param name="model">The pull request review model.</param> public PullRequestReviewViewModel( IPullRequestEditorService editorService, IPullRequestSession session, IPullRequestModel pullRequest, IPullRequestReviewModel model) { Guard.ArgumentNotNull(editorService, nameof(editorService)); Guard.ArgumentNotNull(session, nameof(session)); Guard.ArgumentNotNull(model, nameof(model)); Model = model; Body = string.IsNullOrWhiteSpace(Model.Body) ? null : Model.Body; StateDisplay = ToString(Model.State); var comments = new List <IPullRequestReviewFileCommentViewModel>(); var outdated = new List <IPullRequestReviewFileCommentViewModel>(); foreach (var comment in pullRequest.ReviewComments) { if (comment.PullRequestReviewId == model.Id) { var vm = new PullRequestReviewFileCommentViewModel( editorService, session, comment); if (comment.Position.HasValue) { comments.Add(vm); } else { outdated.Add(vm); } } } FileComments = comments; OutdatedFileComments = outdated; HasDetails = Body != null || FileComments.Count > 0 || OutdatedFileComments.Count > 0; }
IEnumerable <string> GetLocalBranchesInternal( ILocalRepositoryModel localRepository, IRepository repository, IPullRequestModel pullRequest) { if (IsPullRequestFromRepository(localRepository, pullRequest)) { return(new[] { pullRequest.Head.Ref }); } else { var key = BuildGHfVSConfigKeyValue(pullRequest); return(repository.Config .Select(x => new { Branch = BranchCapture.Match(x.Key).Groups["branch"].Value, Value = x.Value }) .Where(x => !string.IsNullOrWhiteSpace(x.Branch) && x.Value == key) .Select(x => x.Branch)); } }
IRepositoryHosts CreateRepositoryHosts(IPullRequestModel pullRequest = null) { var modelService = Substitute.For <IModelService>(); modelService.GetPullRequest(null, null, 0).ReturnsForAnyArgs(x => { var cloneUrl = $"https://github.com/{x.ArgAt<string>(0)}/{x.ArgAt<string>(1)}"; var pr = pullRequest ?? CreatePullRequestModel(x.ArgAt <int>(2), cloneUrl); return(Observable.Return(pr)); }); var repositoryHost = Substitute.For <IRepositoryHost>(); repositoryHost.ModelService.Returns(modelService); var result = Substitute.For <IRepositoryHosts>(); result.LookupHost(null).ReturnsForAnyArgs(repositoryHost); return(result); }
public async Task <string> ExtractToTempFile( ILocalRepositoryModel repository, IPullRequestModel pullRequest, string relativePath, string commitSha, Encoding encoding) { var tempFilePath = CalculateTempFileName(relativePath, commitSha, encoding); if (!File.Exists(tempFilePath)) { using (var repo = gitService.GetRepository(repository.LocalPath)) { var remote = await gitClient.GetHttpRemote(repo, "origin"); await ExtractToTempFile(repo, pullRequest.Number, commitSha, relativePath, encoding, tempFilePath); } } return(tempFilePath); }
public IObservable<Unit> SwitchToBranch(ILocalRepositoryModel repository, IPullRequestModel pullRequest) { return Observable.Defer(async () => { var repo = gitService.GetRepository(repository.LocalPath); var branchName = GetLocalBranchesInternal(repository, repo, pullRequest).FirstOrDefault(); Debug.Assert(branchName != null, "PullRequestService.SwitchToBranch called but no local branch found."); if (branchName != null) { await gitClient.Fetch(repo, "origin"); var branch = repo.Branches[branchName]; if (branch == null) { var trackedBranchName = $"refs/remotes/origin/" + branchName; var trackedBranch = repo.Branches[trackedBranchName]; if (trackedBranch != null) { branch = repo.CreateBranch(branchName, trackedBranch.Tip); await gitClient.SetTrackingBranch(repo, branchName, trackedBranchName); } else { throw new InvalidOperationException($"Could not find branch '{trackedBranchName}'."); } } await gitClient.Checkout(repo, branchName); } return Observable.Empty<Unit>(); }); }
IEnumerable<IPullRequestFileNode> CreateChangedFilesList(IPullRequestModel pullRequest, TreeChanges changes) { return pullRequest.ChangedFiles .Select(x => new PullRequestFileNode(repository.LocalPath, x.FileName, x.Sha, x.Status, GetStatusDisplay(x, changes))); }
public IObservable<TreeChanges> GetTreeChanges(ILocalRepositoryModel repository, IPullRequestModel pullRequest) { return Observable.Defer(async () => { var repo = gitService.GetRepository(repository.LocalPath); await gitClient.Fetch(repo, "origin"); var changes = await gitClient.Compare(repo, pullRequest.Base.Sha, pullRequest.Head.Sha, detectRenames: true); return Observable.Return(changes); }); }
public IObservable<Tuple<string, string>> ExtractDiffFiles( ILocalRepositoryModel repository, IModelService modelService, IPullRequestModel pullRequest, string fileName, string fileSha) { return Observable.Defer(async () => { var repo = gitService.GetRepository(repository.LocalPath); await gitClient.Fetch(repo, "origin"); // The left file is the target of the PR so this should already be fetched. var left = await gitClient.ExtractFile(repo, pullRequest.Base.Sha, fileName); // The right file - if it comes from a fork - may not be fetched so fall back to // getting the file contents from the model service. var right = await GetFileFromRepositoryOrApi(repository, repo, modelService, pullRequest.Head.Sha, fileName, fileSha); if (left == null) { throw new FileNotFoundException($"Could not retrieve {fileName}@{pullRequest.Base.Sha}"); } if (right == null) { throw new FileNotFoundException($"Could not retrieve {fileName}@{pullRequest.Head.Sha}"); } return Observable.Return(Tuple.Create(left, right)); }); }
/// <summary> /// Loads the view model from octokit models. /// </summary> /// <param name="pullRequest">The pull request model.</param> /// <param name="files">The pull request's changed files.</param> public async Task Load(IPullRequestModel pullRequest) { Model = pullRequest; Title = Resources.PullRequestNavigationItemText + " #" + pullRequest.Number; SourceBranchDisplayName = GetBranchDisplayName(pullRequest.Head?.Label); TargetBranchDisplayName = GetBranchDisplayName(pullRequest.Base.Label); Body = !string.IsNullOrWhiteSpace(pullRequest.Body) ? pullRequest.Body : "*No description provided.*"; ChangedFilesTree.Clear(); ChangedFilesList.Clear(); // WPF doesn't support AddRange here so iterate through the changes. foreach (var change in CreateChangedFilesList(pullRequest.ChangedFiles)) { ChangedFilesList.Add(change); } foreach (var change in CreateChangedFilesTree(ChangedFilesList).Children) { ChangedFilesTree.Add(change); } var localBranches = await pullRequestsService.GetLocalBranches(repository, pullRequest).ToList(); var isCheckedOut = localBranches.Contains(repository.CurrentBranch); if (isCheckedOut) { var divergence = await pullRequestsService.CalculateHistoryDivergence(repository, Model.Number); var pullDisabled = divergence.BehindBy == 0 ? "No commits to pull" : null; var pushDisabled = divergence.AheadBy == 0 ? "No commits to push" : divergence.BehindBy > 0 ? "You must pull before you can push" : null; UpdateState = new UpdateCommandState(divergence, pullDisabled, pushDisabled); CheckoutState = null; } else { var caption = localBranches.Count > 0 ? "Checkout " + localBranches.First().DisplayName : "Checkout to " + (await pullRequestsService.GetDefaultLocalBranchName(repository, Model.Number, Model.Title)); var disabled = await pullRequestsService.IsWorkingDirectoryClean(repository) ? null : "Cannot checkout as your working directory has uncommitted changes."; CheckoutState = new CheckoutCommandState(caption, disabled); UpdateState = null; } IsBusy = false; pullRequestsService.RemoteUnusedRemotes(repository).Subscribe(_ => { }); }
public IObservable<Unit> Checkout(ILocalRepositoryModel repository, IPullRequestModel pullRequest, string localBranchName) { return Observable.Defer(async () => { var repo = gitService.GetRepository(repository.LocalPath); var existing = repo.Branches[localBranchName]; if (existing != null) { await gitClient.Checkout(repo, localBranchName); } else if (repository.CloneUrl.ToRepositoryUrl() == pullRequest.Head.RepositoryCloneUrl.ToRepositoryUrl()) { await gitClient.Fetch(repo, "origin"); await gitClient.Checkout(repo, localBranchName); } else { var refSpec = $"{pullRequest.Head.Ref}:{localBranchName}"; var prConfigKey = $"branch.{localBranchName}.{SettingGHfVSPullRequest}"; var remoteName = await CreateRemote(repo, pullRequest.Head.RepositoryCloneUrl); await gitClient.Fetch(repo, remoteName); await gitClient.Fetch(repo, remoteName, new[] { refSpec }); await gitClient.Checkout(repo, localBranchName); await gitClient.SetTrackingBranch(repo, localBranchName, $"refs/remotes/{remoteName}/{pullRequest.Head.Ref}"); await gitClient.SetConfig(repo, prConfigKey, pullRequest.Number.ToString()); } return Observable.Return(Unit.Default); }); }
public IObservable<IBranch> GetLocalBranches(ILocalRepositoryModel repository, IPullRequestModel pullRequest) { return Observable.Defer(() => { var repo = gitService.GetRepository(repository.LocalPath); var result = GetLocalBranchesInternal(repository, repo, pullRequest).Select(x => new BranchModel(x, repository)); return result.ToObservable(); }); }
public bool IsPullRequestFromFork(ILocalRepositoryModel repository, IPullRequestModel pullRequest) { return pullRequest.Head.RepositoryCloneUrl?.ToRepositoryUrl() != repository.CloneUrl.ToRepositoryUrl(); }
public IObservable<Tuple<string, string>> ExtractDiffFiles(ILocalRepositoryModel repository, IPullRequestModel pullRequest, string fileName) { return Observable.Defer(async () => { var repo = gitService.GetRepository(repository.LocalPath); await gitClient.Fetch(repo, "origin"); var left = await gitClient.ExtractFile(repo, pullRequest.Base.Sha, fileName); var right = await gitClient.ExtractFile(repo, pullRequest.Head.Sha, fileName); return Observable.Return(Tuple.Create(left, right)); }); }
IEnumerable<string> GetLocalBranchesInternal( ILocalRepositoryModel localRepository, IRepository repository, IPullRequestModel pullRequest) { if (!IsPullRequestFromFork(localRepository, pullRequest)) { return new[] { pullRequest.Head.Ref }; } else { var pr = pullRequest.Number.ToString(CultureInfo.InvariantCulture); return repository.Config .Select(x => new { Branch = BranchCapture.Match(x.Key).Groups["branch"].Value, Value = x.Value }) .Where(x => !string.IsNullOrWhiteSpace(x.Branch) && x.Value == pr) .Select(x => x.Branch); } }