Skip to content

Commit a271e2d

Browse files
sroy3mattseddon
andauthored
Add a mapping from revision to branches while using only one exp show call (#3980)
* Add a mapping from revision to branches * Fix most tests * Add flag to failing test * Reduce number of calls to git * Rework call to get commit messages * Update structure of code and test fixture * Do not duplicate commits * Fix indicator --------- Co-authored-by: Matt Seddon <[email protected]>
1 parent dfdb939 commit a271e2d

File tree

32 files changed

+1137
-665
lines changed

32 files changed

+1137
-665
lines changed

extension/src/cli/git/reader.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,19 @@ export class GitReader extends GitCli {
4949
return !output
5050
}
5151

52-
public async getCommitMessages(cwd: string, sha: string): Promise<string> {
52+
public async getCommitMessages(
53+
cwd: string,
54+
revision: string,
55+
revisions: string
56+
): Promise<string> {
5357
const options = getOptions(
5458
cwd,
5559
Command.LOG,
56-
sha,
60+
revision,
5761
Flag.PRETTY_FORMAT_COMMIT_MESSAGE,
5862
Flag.SEPARATE_WITH_NULL,
5963
Flag.NUMBER,
60-
'1'
64+
revisions
6165
)
6266
try {
6367
return await this.executeProcess(options)

extension/src/data/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@ import { uniqueValues } from '../util/array'
99
import { DeferredDisposable } from '../class/deferred'
1010
import { isSameOrChild } from '../fileSystem'
1111

12+
export type ExperimentsOutput = {
13+
expShow: ExpShowOutput
14+
rowOrder: { branch: string; sha: string }[]
15+
gitLog: string
16+
}
17+
1218
export abstract class BaseData<
13-
T extends { data: PlotsOutputOrError; revs: string[] } | ExpShowOutput
19+
T extends { data: PlotsOutputOrError; revs: string[] } | ExperimentsOutput
1420
> extends DeferredDisposable {
1521
public readonly onDidUpdate: Event<T>
1622
public readonly onDidChangeDvcYaml: Event<void>

extension/src/experiments/data/index.ts

Lines changed: 61 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import {
77
import { getRelativePattern } from '../../fileSystem/relativePattern'
88
import { createFileSystemWatcher } from '../../fileSystem/watcher'
99
import { AvailableCommands, InternalCommands } from '../../commands/internal'
10-
import { EXPERIMENT_WORKSPACE_ID, ExpShowOutput } from '../../cli/dvc/contract'
11-
import { BaseData } from '../../data'
12-
import { DOT_DVC, ExperimentFlag } from '../../cli/dvc/constants'
13-
import { gitPath } from '../../cli/git/constants'
10+
import { ExpShowOutput } from '../../cli/dvc/contract'
11+
import { BaseData, ExperimentsOutput } from '../../data'
12+
import { Args, DOT_DVC, ExperimentFlag } from '../../cli/dvc/constants'
13+
import { COMMITS_SEPARATOR, gitPath } from '../../cli/git/constants'
1414
import { getGitPath } from '../../fileSystem'
1515
import { ExperimentsModel } from '../model'
1616

17-
export class ExperimentsData extends BaseData<ExpShowOutput> {
17+
export class ExperimentsData extends BaseData<ExperimentsOutput> {
1818
private readonly experiments: ExperimentsModel
1919

2020
constructor(
@@ -47,69 +47,80 @@ export class ExperimentsData extends BaseData<ExpShowOutput> {
4747
)
4848

4949
void this.updateAvailableBranchesToSelect(allBranches)
50-
const data: ExpShowOutput = []
5150

52-
const { branches, currentBranch } = await this.getBranchesToShowWithCurrent(
53-
allBranches
54-
)
51+
const branches = await this.getBranchesToShow(allBranches)
52+
let gitLog = ''
53+
const rowOrder: { branch: string; sha: string }[] = []
54+
const args: Args = []
5555

56-
await Promise.all(
57-
branches.map(async branch => {
58-
const branchFlags = [
59-
ExperimentFlag.REV,
60-
branch,
61-
ExperimentFlag.NUM_COMMIT,
62-
this.experiments.getNbOfCommitsToShow(branch).toString()
63-
]
64-
65-
const output = (await this.expShow(
66-
branchFlags,
67-
branch
68-
)) as ExpShowOutput
69-
70-
if (branch !== currentBranch) {
71-
const workspaceIndex = output.findIndex(
72-
exp => exp.rev === EXPERIMENT_WORKSPACE_ID
73-
)
74-
output.splice(workspaceIndex, 1)
75-
}
76-
data.push(...output)
77-
})
56+
for (const branch of branches) {
57+
gitLog = await this.collectGitLogAndOrder(gitLog, branch, rowOrder, args)
58+
}
59+
60+
const expShow = await this.internalCommands.executeCommand<ExpShowOutput>(
61+
AvailableCommands.EXP_SHOW,
62+
this.dvcRoot,
63+
...args
7864
)
7965

80-
this.collectFiles(data)
66+
this.collectFiles({ expShow })
8167

82-
return this.notifyChanged(data)
68+
return this.notifyChanged({
69+
expShow,
70+
gitLog,
71+
rowOrder
72+
})
8373
}
8474

85-
protected collectFiles(data: ExpShowOutput) {
86-
this.collectedFiles = collectFiles(data, this.collectedFiles)
75+
protected collectFiles({ expShow }: { expShow: ExpShowOutput }) {
76+
this.collectedFiles = collectFiles(expShow, this.collectedFiles)
8777
}
8878

89-
private async getBranchesToShowWithCurrent(allBranches: string[]) {
79+
private async collectGitLogAndOrder(
80+
gitLog: string,
81+
branch: string,
82+
rowOrder: { branch: string; sha: string }[],
83+
args: Args
84+
) {
85+
const nbOfCommitsToShow = this.experiments.getNbOfCommitsToShow(branch)
86+
87+
const branchGitLog = await this.internalCommands.executeCommand(
88+
AvailableCommands.GIT_GET_COMMIT_MESSAGES,
89+
this.dvcRoot,
90+
branch,
91+
String(nbOfCommitsToShow)
92+
)
93+
gitLog = [gitLog, branchGitLog].join(COMMITS_SEPARATOR)
94+
95+
for (const commit of branchGitLog.split(COMMITS_SEPARATOR)) {
96+
const [sha] = commit.split('\n')
97+
rowOrder.push({ branch, sha })
98+
if (args.includes(sha)) {
99+
continue
100+
}
101+
args.push(ExperimentFlag.REV, sha)
102+
}
103+
return gitLog
104+
}
105+
106+
private async getBranchesToShow(allBranches: string[]) {
90107
const currentBranch = await this.internalCommands.executeCommand<string>(
91108
AvailableCommands.GIT_GET_CURRENT_BRANCH,
92109
this.dvcRoot
93110
)
94111

95112
this.experiments.pruneBranchesToShow(allBranches)
96113

97-
const branches = this.experiments.getBranchesToShow()
114+
const currentBranches = [
115+
currentBranch,
116+
...this.experiments
117+
.getBranchesToShow()
118+
.filter(branch => branch !== currentBranch)
119+
]
98120

99-
if (!branches.includes(currentBranch)) {
100-
branches.push(currentBranch)
101-
this.experiments.setBranchesToShow(branches)
102-
}
103-
return { branches, currentBranch }
104-
}
121+
this.experiments.setBranchesToShow(currentBranches)
105122

106-
private async expShow(flags: (ExperimentFlag | string)[], branch?: string) {
107-
const data = await this.internalCommands.executeCommand<ExpShowOutput>(
108-
AvailableCommands.EXP_SHOW,
109-
this.dvcRoot,
110-
...flags
111-
)
112-
return data.map(exp => ({ ...exp, branch }))
123+
return currentBranches
113124
}
114125

115126
private async updateAvailableBranchesToSelect(branches?: string[]) {

extension/src/experiments/index.ts

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { DecorationProvider } from './model/decorationProvider'
3333
import { starredFilter } from './model/filterBy/constants'
3434
import { ResourceLocator } from '../resourceLocator'
3535
import { AvailableCommands, InternalCommands } from '../commands/internal'
36-
import { EXPERIMENT_WORKSPACE_ID, ExpShowOutput } from '../cli/dvc/contract'
36+
import { ExperimentsOutput } from '../data'
3737
import { ViewKey } from '../webview/constants'
3838
import { BaseRepository } from '../webview/repository'
3939
import { Title } from '../vscode/title'
@@ -43,7 +43,6 @@ import { Toast } from '../vscode/toast'
4343
import { ConfigKey } from '../vscode/config'
4444
import { checkSignalFile, pollSignalFileForProcess } from '../fileSystem'
4545
import { DVCLIVE_ONLY_RUNNING_SIGNAL_FILE } from '../cli/dvc/constants'
46-
import { COMMITS_SEPARATOR } from '../cli/git/constants'
4746

4847
export const ExperimentsScale = {
4948
...omit(ColumnType, 'TIMESTAMP'),
@@ -169,13 +168,12 @@ export class Experiments extends BaseRepository<TableData> {
169168
return this.data.managedUpdate()
170169
}
171170

172-
public async setState(data: ExpShowOutput) {
171+
public async setState({ expShow, gitLog, rowOrder }: ExperimentsOutput) {
173172
const hadCheckpoints = this.hasCheckpoints()
174173
const dvcLiveOnly = await this.checkSignalFile()
175-
const commitsOutput = await this.getCommitOutput(data)
176174
await Promise.all([
177-
this.columns.transformAndSet(data),
178-
this.experiments.transformAndSet(data, dvcLiveOnly, commitsOutput)
175+
this.columns.transformAndSet(expShow),
176+
this.experiments.transformAndSet(expShow, gitLog, dvcLiveOnly, rowOrder)
179177
])
180178

181179
if (hadCheckpoints !== this.hasCheckpoints()) {
@@ -538,25 +536,6 @@ export class Experiments extends BaseRepository<TableData> {
538536
return this.webviewMessages.sendWebviewMessage()
539537
}
540538

541-
private async getCommitOutput(data: ExpShowOutput | undefined) {
542-
if (!data || data.length === 0) {
543-
return
544-
}
545-
let output = ''
546-
for (const commit of data) {
547-
if (commit.rev === EXPERIMENT_WORKSPACE_ID) {
548-
continue
549-
}
550-
output += await this.internalCommands.executeCommand(
551-
AvailableCommands.GIT_GET_COMMIT_MESSAGES,
552-
this.dvcRoot,
553-
commit.rev
554-
)
555-
output += COMMITS_SEPARATOR
556-
}
557-
return output
558-
}
559-
560539
private createWebviewMessageHandler() {
561540
const webviewMessages = new WebviewMessages(
562541
this.dvcRoot,
@@ -596,7 +575,7 @@ export class Experiments extends BaseRepository<TableData> {
596575
}
597576

598577
return await pickExperiment(
599-
this.experiments.getUniqueList(),
578+
this.experiments.getCombinedList(),
600579
this.getFirstThreeColumnOrder(),
601580
Title.SELECT_BASE_EXPERIMENT
602581
)

extension/src/experiments/model/collect.test.ts

Lines changed: 25 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,64 @@
11
import { collectExperiments } from './collect'
2-
import { COMMITS_SEPARATOR } from '../../cli/git/constants'
32
import { generateTestExpShowOutput } from '../../test/util/experiments'
3+
import { ExpShowOutput } from '../../cli/dvc/contract'
4+
5+
const DEFAULT_DATA: [string, boolean] = ['', false]
46

57
describe('collectExperiments', () => {
68
it('should return an empty array if no commits are present', () => {
79
const { commits } = collectExperiments(
810
generateTestExpShowOutput({}),
9-
false,
10-
''
11+
...DEFAULT_DATA
1112
)
1213
expect(commits).toStrictEqual([])
1314
})
1415

15-
const expShowWithTwoCommits = generateTestExpShowOutput(
16-
{},
17-
{
18-
experiments: [{}, {}],
19-
name: 'branchA',
20-
rev: '61bed4ce8913eca7f73ca754d65bc5daad1520e2'
21-
},
22-
{
23-
name: 'branchB',
24-
rev: '351e42ace3cb6a3a853c65bef285e60748cc6341'
25-
}
26-
)
16+
const expShowWithTwoCommits: [ExpShowOutput, string, boolean] = [
17+
generateTestExpShowOutput(
18+
{},
19+
{
20+
experiments: [{}, {}],
21+
name: 'branchA',
22+
rev: '61bed4ce8913eca7f73ca754d65bc5daad1520e2'
23+
},
24+
{
25+
name: 'branchB',
26+
rev: '351e42ace3cb6a3a853c65bef285e60748cc6341'
27+
}
28+
),
29+
...DEFAULT_DATA
30+
]
2731

2832
it('should define a workspace', () => {
29-
const { workspace } = collectExperiments(expShowWithTwoCommits, false, '')
33+
const { workspace } = collectExperiments(...expShowWithTwoCommits)
3034

3135
expect(workspace).toBeDefined()
3236
})
3337

3438
it('should find two branches from a repo with two branches', () => {
35-
const { commits } = collectExperiments(expShowWithTwoCommits, false, '')
39+
const { commits } = collectExperiments(...expShowWithTwoCommits)
3640

3741
expect(commits.length).toStrictEqual(2)
3842
})
3943

4044
it('should list commits in the same order as they are collected', () => {
41-
const { commits } = collectExperiments(expShowWithTwoCommits, false, '')
45+
const { commits } = collectExperiments(...expShowWithTwoCommits)
4246
const [commitA, commitB] = commits
4347

4448
expect(commitA.id).toStrictEqual('branchA')
4549
expect(commitB.id).toStrictEqual('branchB')
4650
})
4751

4852
it('should find two experiments on commitA', () => {
49-
const { experimentsByCommit } = collectExperiments(
50-
expShowWithTwoCommits,
51-
false,
52-
''
53-
)
53+
const { experimentsByCommit } = collectExperiments(...expShowWithTwoCommits)
5454
expect(experimentsByCommit.get('branchA')?.length).toStrictEqual(2)
5555
})
5656

5757
it('should find no experiments on branchB', () => {
58-
const { experimentsByCommit } = collectExperiments(
59-
expShowWithTwoCommits,
60-
false,
61-
''
62-
)
58+
const { experimentsByCommit } = collectExperiments(...expShowWithTwoCommits)
6359
expect(experimentsByCommit.get('branchB')).toBeUndefined()
6460
})
6561

66-
it('should add data from git to commits if git log output is provided', () => {
67-
const { commits } = collectExperiments(
68-
expShowWithTwoCommits,
69-
false,
70-
`61bed4ce8913eca7f73ca754d65bc5daad1520e2\nJohn Smith\n3 days ago\nrefNames:tag: v.1.1\nmessage:add new feature${COMMITS_SEPARATOR}351e42ace3cb6a3a853c65bef285e60748cc6341\nrenovate[bot]\n5 weeks ago\nrefNames:\nmessage:update various dependencies\n* update dvc\n* update dvclive`
71-
)
72-
const [branch1, branch2] = commits
73-
expect(branch1.description).toStrictEqual('add new feature')
74-
expect(branch2.description).toStrictEqual('update various dependencies ...')
75-
expect(branch1.commit).toStrictEqual({
76-
author: 'John Smith',
77-
date: '3 days ago',
78-
message: 'add new feature',
79-
tags: ['v.1.1']
80-
})
81-
expect(branch2.commit).toStrictEqual({
82-
author: 'renovate[bot]',
83-
date: '5 weeks ago',
84-
message: 'update various dependencies\n* update dvc\n* update dvclive',
85-
tags: []
86-
})
87-
})
88-
8962
it('should not collect the same experiment twice', () => {
9063
const main = {
9164
experiments: [
@@ -114,8 +87,7 @@ describe('collectExperiments', () => {
11487

11588
const { experimentsByCommit, commits } = collectExperiments(
11689
expShowWithDuplicateCommits,
117-
false,
118-
''
90+
...DEFAULT_DATA
11991
)
12092

12193
expect(commits.length).toStrictEqual(3)

0 commit comments

Comments
 (0)