Skip to content

Commit 4de3e1a

Browse files
committed
Fix "Pull all" button when a linked worktree has changes
Also move the logic to app-store where it belongs
1 parent 278ba67 commit 4de3e1a

5 files changed

Lines changed: 45 additions & 51 deletions

File tree

app/src/lib/git/for-each-ref.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ export async function getBranchesDifferingFromUpstream(
9191
upstream: '%(upstream)',
9292
symref: '%(symref)',
9393
head: '%(HEAD)',
94+
worktreePath: '%(worktreepath)',
9495
})
9596

9697
const prefixes = ['refs/heads', 'refs/remotes']
@@ -117,6 +118,10 @@ export async function getBranchesDifferingFromUpstream(
117118
// Exclude symbolic refs and the current branch
118119
continue
119120
}
121+
if (ref.worktreePath.length > 0 && ref.worktreePath !== repository.path) {
122+
// Exclude branches checked out in other worktrees, since they can't be fast-forwarded from here
123+
continue
124+
}
120125

121126
if (ref.fullName.startsWith('refs/heads')) {
122127
if (ref.upstream.length === 0) {

app/src/lib/stores/app-store.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5663,12 +5663,39 @@ export class AppStore extends TypedBaseStore<IAppState> {
56635663
}
56645664
}
56655665

5666+
private async withRepoInfoInError(
5667+
errorMessagePrefix: string,
5668+
repository: Repository,
5669+
fn: () => Promise<void>
5670+
): Promise<void> {
5671+
try {
5672+
await fn()
5673+
} catch (error) {
5674+
const message = error instanceof Error ? error.message : String(error)
5675+
throw new Error(
5676+
`${errorMessagePrefix} '${repository.name}' (${repository.path}): ${message}`,
5677+
{ cause: error }
5678+
)
5679+
}
5680+
}
5681+
56665682
public async _pull(repository: Repository): Promise<void> {
56675683
return this.withRefreshedGitHubRepository(repository, repository => {
56685684
return this.performPull(repository)
56695685
})
56705686
}
56715687

5688+
public async _pullAllRepositories(): Promise<void> {
5689+
const repositories = await this.repositoriesStore.getAll()
5690+
await Promise.all(
5691+
repositories.map(repository =>
5692+
this.withRepoInfoInError('Error pulling', repository, () =>
5693+
this._pull(repository)
5694+
)
5695+
)
5696+
)
5697+
}
5698+
56725699
/** This shouldn't be called directly. See `Dispatcher`. */
56735700
private async performPull(repository: Repository): Promise<void> {
56745701
return this.withPushPullFetch(repository, async () => {

app/src/models/repository.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ export class Repository {
113113
}
114114

115115
public get path(): string {
116+
// NOTE: This is not actually the main worktree. We preserve the name "mainWorkTree" to
117+
// minimize merge conflicts when pulling changes from the official repo (desktop/desktop).
118+
// If isLinkedWorktree = true, this is actually the path to the linked worktree
116119
return this.mainWorkTree.path
117120
}
118121

app/src/ui/dispatcher/dispatcher.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,14 @@ export class Dispatcher {
780780
return this.appStore._pull(repository)
781781
}
782782

783+
public async pullAllRepositories(): Promise<void> {
784+
try {
785+
await this.appStore._pullAllRepositories()
786+
} catch (error) {
787+
this.postError(error)
788+
}
789+
}
790+
783791
/** Fetch a specific refspec for the repository. */
784792
public fetchRefspec(
785793
repository: Repository,

app/src/ui/repositories-list/repositories-list.tsx

Lines changed: 2 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -126,36 +126,6 @@ function findMatchingListItem(
126126
return null
127127
}
128128

129-
function isPullableRepository(
130-
repository: Repositoryish,
131-
repositories: ReadonlyArray<Repositoryish>
132-
): repository is Repository {
133-
if (!(repository instanceof Repository)) {
134-
return false
135-
}
136-
137-
if (!repository.isLinkedWorktree) {
138-
return true
139-
}
140-
141-
const mainWorktreePath = normalizePath(repository.mainWorktreePath)
142-
const candidatesWithSameMain = repositories.filter(
143-
(candidate): candidate is Repository =>
144-
candidate instanceof Repository &&
145-
normalizePath(candidate.mainWorktreePath) === mainWorktreePath
146-
)
147-
148-
if (candidatesWithSameMain.length === 0) {
149-
return false
150-
}
151-
152-
const preferred =
153-
candidatesWithSameMain.find(candidate => !candidate.isLinkedWorktree) ??
154-
candidatesWithSameMain[0]
155-
156-
return preferred.id === repository.id
157-
}
158-
159129
/** The list of user-added repositories. */
160130
export class RepositoriesList extends React.Component<
161131
IRepositoriesListProps,
@@ -682,27 +652,8 @@ export class RepositoriesList extends React.Component<
682652

683653
private onPullRepositoriesButtonClick = async () => {
684654
this.setState({ pullingRepositories: true })
685-
try {
686-
const repositoriesToPull = this.props.repositories.filter(repository =>
687-
isPullableRepository(repository, this.props.repositories)
688-
)
689-
690-
await Promise.all(
691-
repositoriesToPull.map(repository =>
692-
this.props.dispatcher.pull(repository).catch(e => {
693-
const message = e instanceof Error ? e.message : String(e)
694-
throw new Error(
695-
`Error pulling '${repository.name}' (${repository.path}): ${message}`,
696-
{ cause: e }
697-
)
698-
})
699-
)
700-
)
701-
} catch (e) {
702-
this.props.dispatcher.postError(e)
703-
} finally {
704-
this.setState({ pullingRepositories: false })
705-
}
655+
await this.props.dispatcher.pullAllRepositories()
656+
this.setState({ pullingRepositories: false })
706657
}
707658

708659
private onCloneRepository = () => {

0 commit comments

Comments
 (0)