Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/src/lib/app-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { IStashEntry } from '../models/stash-entry'
import { TutorialStep } from '../models/tutorial-step'
import { UncommittedChangesStrategy } from '../models/uncommitted-changes-strategy'
import { ShowBranchNameInRepoListSetting } from '../models/show-branch-name-in-repo-list'
import { BranchSortOrder } from '../models/branch-sort-order'
import { DragElement } from '../models/drag-drop'
import { ILastThankYou } from '../models/last-thank-you'
import {
Expand Down Expand Up @@ -398,6 +399,9 @@ export interface IAppState {
/** Controls when to show the current branch name next to each repository in the repository list */
readonly showBranchNameInRepoList: ShowBranchNameInRepoListSetting

/** Controls the sort order for branch lists in branch-selection views */
readonly branchSortOrder: BranchSortOrder

/**
* Cached repo rulesets. Used to prevent repeatedly querying the same
* rulesets to check their bypass status.
Expand Down
49 changes: 42 additions & 7 deletions app/src/lib/stores/app-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ import {
defaultShowBranchNameInRepoListSetting,
ShowBranchNameInRepoListSetting,
} from '../../models/show-branch-name-in-repo-list'
import {
BranchSortOrder,
defaultBranchSortOrder,
} from '../../models/branch-sort-order'
import { WorkflowPreferences } from '../../models/workflow-preferences'
import { TrashNameLabel } from '../../ui/lib/context-menu'
import { getDefaultDir } from '../../ui/lib/default-dir'
Expand Down Expand Up @@ -494,6 +498,7 @@ export const showDiffCheckMarksDefault = true
export const showDiffCheckMarksKey = 'diff-check-marks-visible'

export const showBranchNameInRepoListKey = 'show-branch-name-in-repo-list'
const branchSortOrderKey = 'branch-sort-order'

const commitMessageGenerationDisclaimerLastSeenKey =
'commit-message-generation-disclaimer-last-seen'
Expand Down Expand Up @@ -663,6 +668,8 @@ export class AppStore extends TypedBaseStore<IAppState> {
private showBranchNameInRepoList: ShowBranchNameInRepoListSetting =
defaultShowBranchNameInRepoListSetting

private branchSortOrder: BranchSortOrder = defaultBranchSortOrder

private cachedRepoRulesets = new Map<number, IAPIRepoRuleset>()

private underlineLinks: boolean = underlineLinksDefault
Expand Down Expand Up @@ -1212,6 +1219,7 @@ export class AppStore extends TypedBaseStore<IAppState> {
underlineLinks: this.underlineLinks,
showDiffCheckMarks: this.showDiffCheckMarks,
showBranchNameInRepoList: this.showBranchNameInRepoList,
branchSortOrder: this.branchSortOrder,
updateState: updateStore.state,
commitMessageGenerationDisclaimerLastSeen:
this.commitMessageGenerationDisclaimerLastSeen,
Expand Down Expand Up @@ -2619,6 +2627,9 @@ export class AppStore extends TypedBaseStore<IAppState> {
getEnum(showBranchNameInRepoListKey, ShowBranchNameInRepoListSetting) ??
defaultShowBranchNameInRepoListSetting

this.branchSortOrder =
getEnum(branchSortOrderKey, BranchSortOrder) ?? defaultBranchSortOrder

this.commitMessageGenerationDisclaimerLastSeen =
getNumber(commitMessageGenerationDisclaimerLastSeenKey) ?? null

Expand Down Expand Up @@ -3929,7 +3940,9 @@ export class AppStore extends TypedBaseStore<IAppState> {

// loadBranches needs the default remote to determine the default branch
await gitStore.loadRemotes()
await gitStore.loadBranches()
await gitStore.loadBranches(
gitStore.getRecentBranchesLimit(this.branchSortOrder)
)
await gitStore.loadWorktrees()

const section = state.selectedSection
Expand Down Expand Up @@ -7520,12 +7533,17 @@ export class AppStore extends TypedBaseStore<IAppState> {

// prettier-ignore
const PR_URLS = {
bitbucket:
`${htmlURL}/pull-requests/new?${param('source', encodedCompareBranch)}&${param('dest', encodedBaseBranch)}`,
github:
`${htmlURL}/pull/new/${encodedBaseBranch ? encodedBaseBranch + '...' : ''}${encodedCompareBranch}`,
gitlab:
`${htmlURL}/merge_requests/new?${param('merge_request[source_branch]', encodedCompareBranch)}&${param('merge_request[target_branch]', encodedBaseBranch)}`,
bitbucket: `${htmlURL}/pull-requests/new?${param(
'source',
encodedCompareBranch
)}&${param('dest', encodedBaseBranch)}`,
github: `${htmlURL}/pull/new/${
encodedBaseBranch ? encodedBaseBranch + '...' : ''
}${encodedCompareBranch}`,
gitlab: `${htmlURL}/merge_requests/new?${param(
'merge_request[source_branch]',
encodedCompareBranch
)}&${param('merge_request[target_branch]', encodedBaseBranch)}`,
Comment on lines +7536 to +7546
Copy link
Owner

@pol-rivero pol-rivero Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change the formatting in these lines? There doesn't seem to be any content changes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've run prettier locally and it has changed the lines. GitHub Copilot then commented on the formatting, so not really sure what happened. I can revert.

Copy link
Owner

@pol-rivero pol-rivero Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's strange, PR_URLS has a // prettier-ignore comment, so it should be ignored...

I added that exception because these constructed URLs become practically unreadable when formatted.

}
await this._openInBrowser(PR_URLS[type])
}
Expand Down Expand Up @@ -9202,6 +9220,23 @@ export class AppStore extends TypedBaseStore<IAppState> {
}
}

public _updateBranchSortOrder(branchSortOrder: BranchSortOrder) {
if (branchSortOrder !== this.branchSortOrder) {
this.branchSortOrder = branchSortOrder
localStorage.setItem(branchSortOrderKey, branchSortOrder)
this.emitUpdate()

if (this.selectedRepository instanceof Repository) {
const gitStore = this.gitStoreCache.get(this.selectedRepository)
if (gitStore) {
void gitStore.loadBranches(
gitStore.getRecentBranchesLimit(branchSortOrder)
)
}
}
}
}

public _updateFileListFilter(
repository: Repository,
filterUpdate: Partial<IFileListFilterState>
Expand Down
30 changes: 23 additions & 7 deletions app/src/lib/stores/git-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,22 @@ import { findDefaultBranch } from '../find-default-branch'
import { cleanUntrackedFiles } from '../git/clean'
import { dotGitPath } from '../helpers/git-dir'
import { normalizePath } from '../helpers/path'
import { BranchSortOrder } from '../../models/branch-sort-order'

/** The number of commits to load from history per batch. */
const CommitBatchSize = 100
const CommitBatchSizeSearch = 500

const LoadingHistoryRequestKey = 'history'

/** The max number of recent branches to find. */
const RecentBranchesLimit = 5
/**
* The max number of recent branches to include for non-alphabetic
* sorts where full "recent" list ordering is required.
*/
const RecentOptionsBranchLimit = 2500
Copy link
Owner

@pol-rivero pol-rivero Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2500 seems like an arbitrary limit, and some repos can have way more branches. There should be no limit.
I think that the current approach of just extending the "Recent branches" section is flawed. It would be much better to just hide the "Recent branches" section and change the sorting of the "Other branches" section. This might give you the grouping of local branches at the top (see my first comment) "for free".


/** The max number of recent branches to include for alphabetic sort mode. */
const AlphabetBranchRecentLimit = 5

/** The store for a repository's git data. */
export class GitStore extends BaseStore {
Expand Down Expand Up @@ -178,6 +185,12 @@ export class GitStore extends BaseStore {
this._tagsToPush = getTagsToPush(repository)
}

public getRecentBranchesLimit(branchSortOrder: BranchSortOrder): number {
return branchSortOrder === BranchSortOrder.Alphabet
? AlphabetBranchRecentLimit
: RecentOptionsBranchLimit
}

/**
* Reconcile the local history view with the repository state
* after a pull has completed, to include merged remote commits.
Expand Down Expand Up @@ -398,14 +411,16 @@ export class GitStore extends BaseStore {
}

/** Load all the branches. */
public async loadBranches() {
public async loadBranches(
recentBranchesLimit: number = AlphabetBranchRecentLimit
) {
const [localAndRemoteBranches, recentBranchNames] = await Promise.all([
this.performFailableOperation(() => getBranches(this.repository)) || [],
this.performFailableOperation(() =>
// Chances are that the recent branches list will contain the default
// branch which we filter out in refreshRecentBranches. So grab one
// more than we need to account for that.
getRecentBranches(this.repository, RecentBranchesLimit + 1)
getRecentBranches(this.repository, recentBranchesLimit + 1)
),
])

Expand All @@ -417,7 +432,7 @@ export class GitStore extends BaseStore {

// refreshRecentBranches is dependent on having a default branch
await this.refreshDefaultBranch()
this.refreshRecentBranches(recentBranchNames)
this.refreshRecentBranches(recentBranchNames, recentBranchesLimit)

await this.checkPullWithRebase()

Expand Down Expand Up @@ -558,7 +573,8 @@ export class GitStore extends BaseStore {
}

private refreshRecentBranches(
recentBranchNames: ReadonlyArray<string> | undefined
recentBranchNames: ReadonlyArray<string> | undefined,
recentBranchesLimit: number = RecentOptionsBranchLimit
) {
if (!recentBranchNames || !recentBranchNames.length) {
this._recentBranches = []
Expand Down Expand Up @@ -591,7 +607,7 @@ export class GitStore extends BaseStore {

recentBranches.push(branch)

if (recentBranches.length >= RecentBranchesLimit) {
if (recentBranches.length >= recentBranchesLimit) {
break
}
}
Expand Down
7 changes: 7 additions & 0 deletions app/src/models/branch-sort-order.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export enum BranchSortOrder {
Alphabet = 'Alphabet',
RecentlyAdded = 'RecentlyAdded',
RecentlyChanged = 'RecentlyChanged',
}

export const defaultBranchSortOrder = BranchSortOrder.Alphabet
3 changes: 3 additions & 0 deletions app/src/ui/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1659,6 +1659,7 @@ export class App extends React.Component<IAppProps, IAppState> {
underlineLinks={this.state.underlineLinks}
showDiffCheckMarks={this.state.showDiffCheckMarks}
showBranchNameInRepoList={this.state.showBranchNameInRepoList}
branchSortOrder={this.state.branchSortOrder}
/>
)
case PopupType.RepositorySettings: {
Expand Down Expand Up @@ -3457,6 +3458,7 @@ export class App extends React.Component<IAppProps, IAppState> {
dispatcher={this.props.dispatcher}
isOpen={isOpen}
branchDropdownWidth={this.state.branchDropdownWidth}
branchSortOrder={this.state.branchSortOrder}
onDropDownStateChanged={this.onBranchDropdownStateChanged}
repository={repository}
repositoryState={selection.state}
Expand Down Expand Up @@ -3635,6 +3637,7 @@ export class App extends React.Component<IAppProps, IAppState> {
stashedFilesWidth={state.stashedFilesWidth}
issuesStore={this.props.issuesStore}
gitHubUserStore={this.props.gitHubUserStore}
branchSortOrder={state.branchSortOrder}
onViewCommitOnGitHub={this.onViewCommitOnGitHub}
imageDiffType={state.imageDiffType}
hideWhitespaceInChangesDiff={state.hideWhitespaceInChangesDiff}
Expand Down
7 changes: 6 additions & 1 deletion app/src/ui/branches/branch-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { SelectionSource } from '../lib/filter-list'
import { IMatches } from '../../lib/fuzzy-find'
import { Button } from '../lib/button'
import { TextBox } from '../lib/text-box'
import { BranchSortOrder } from '../../models/branch-sort-order'

import {
groupBranches,
Expand Down Expand Up @@ -53,6 +54,8 @@ interface IBranchListProps {
*/
readonly recentBranches: ReadonlyArray<Branch>

readonly branchSortOrder?: BranchSortOrder

/**
* All worktrees in the repository.
*/
Expand Down Expand Up @@ -195,7 +198,9 @@ export class BranchList extends React.Component<
this.props.currentBranch,
this.props.allBranches,
this.props.recentBranches,
this.props.allWorktrees
this.props.allWorktrees,
this.props.branchSortOrder,
this.state.commitAuthorDates
)
}

Expand Down
4 changes: 4 additions & 0 deletions app/src/ui/branches/branches-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { Button } from '../lib/button'
import { BranchList } from './branch-list'
import { PullRequestList } from './pull-request-list'
import { IBranchListItem } from './group-branches'
import { BranchSortOrder } from '../../models/branch-sort-order'
import {
getDefaultAriaLabelForBranch,
renderDefaultBranch,
Expand Down Expand Up @@ -53,6 +54,8 @@ interface IBranchesContainerProps {
readonly onSetAsDefaultBranch: (branchName: string) => void
readonly onDeleteBranch: (branchName: string) => void

readonly branchSortOrder: BranchSortOrder

/** All worktrees in the repository. */
readonly allWorktrees: ReadonlyArray<WorktreeEntry>

Expand Down Expand Up @@ -280,6 +283,7 @@ export class BranchesContainer extends React.Component<
currentBranch={this.props.currentBranch}
allBranches={this.props.allBranches}
recentBranches={this.props.recentBranches}
branchSortOrder={this.props.branchSortOrder}
allWorktrees={this.props.allWorktrees}
onItemClick={this.onBranchItemClick}
filterText={this.state.branchFilterText}
Expand Down
Loading
Loading