mirror of
https://github.com/facebook/sapling.git
synced 2025-01-03 02:25:40 +03:00
stackEdit: add api to set absorb destination
Summary: Make it possible to move the absorb edit to a different destination commit. Reviewed By: muirdm Differential Revision: D67612324 fbshipit-source-id: b0b6c76f1ec74605f4b385e1dd505b8720396b1a
This commit is contained in:
parent
2d22f73947
commit
0d8111a9e5
@ -1213,6 +1213,39 @@ describe('CommitStackState', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('updates absorb destination commit', () => {
|
||||
const stack = new CommitStackState(absorbStack2).analyseAbsorb();
|
||||
// Move the "a1 -> A1" change from CommitA to CommitC.
|
||||
// See the above test's "describeAbsorbExtra" to confirm that "a1 -> A1"
|
||||
// has fileIdx=0 and absorbEditId=0.
|
||||
// CommitC has rev=3.
|
||||
const newStack = stack.setAbsorbEditDestination(0, 0, 3);
|
||||
// "-a1 +A1" now has "Selected=2":
|
||||
expect(describeAbsorbExtra(newStack)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"0": [
|
||||
"0: -a1↵ +A1↵ Selected=2 Introduced=1",
|
||||
"1: -x1↵ +X1↵ Selected=2 Introduced=2",
|
||||
],
|
||||
"1": [
|
||||
"0: -b1↵ +B1↵ Selected=1 Introduced=1",
|
||||
"1: -y1↵ +Y1↵ Selected=2 Introduced=2",
|
||||
],
|
||||
"2": [
|
||||
"0: -c1↵ c2↵ +C1↵ C2↵ Introduced=0",
|
||||
],
|
||||
}
|
||||
`);
|
||||
// The A1 is now absorbed at CommitC.
|
||||
expect(newStack.describeFileStacks()).toMatchInlineSnapshot(`
|
||||
[
|
||||
"0:./a.txt 1:CommitA/a.txt(a1↵a2↵a3↵) 2:CommitC/a.txt(a1↵a2↵a3↵x1↵;absorbed:A1↵a2↵a3↵X1↵)",
|
||||
"0:./b.txt 1:CommitB/b.txt(b1↵b2↵b3↵;absorbed:B1↵b2↵b3↵) 2:CommitC/b.txt(B1↵b2↵b3↵y1↵;absorbed:B1↵b2↵b3↵Y1↵)",
|
||||
"0:./c.txt(c1↵c2↵c3↵) 1:CommitC/c.txt(c1↵c2↵c3↵z1↵) 2:Wdir/c.txt(c1↵c2↵c3↵z1↵;absorbed:C1↵C2↵c3↵z1↵)",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
function describeAbsorbExtra(stack: CommitStackState) {
|
||||
return stack.absorbExtra.map(describeAbsorbIdChunkMap).toJS();
|
||||
}
|
||||
|
@ -27,7 +27,12 @@ import {
|
||||
import {t} from '../i18n';
|
||||
import {readAtom} from '../jotaiUtils';
|
||||
import {assert} from '../utils';
|
||||
import {calculateAbsorbEditsForFileStack, revWithAbsorb} from './absorb';
|
||||
import {
|
||||
calculateAbsorbEditsForFileStack,
|
||||
embedAbsorbId,
|
||||
extractRevAbsorbId,
|
||||
revWithAbsorb,
|
||||
} from './absorb';
|
||||
import {FileStackState} from './fileStackState';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
import {Seq, List, Map as ImMap, Set as ImSet, Record, is} from 'immutable';
|
||||
@ -612,6 +617,61 @@ export class CommitStackState extends SelfUpdate<CommitStackRecord> {
|
||||
return {selectedRev, candidateRevs};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set `rev` as the "target commit" (amend --to) of an "absorb edit".
|
||||
* Happens when the user moves the absorb edit among candidate commits.
|
||||
*
|
||||
* Throws if the edit cannot be fulfilled, for example, the `commitRev` is
|
||||
* before the commit introducing the change (conflict), or if the `commitRev`
|
||||
* does not touch the file being edited (current limiation, might be lifted).
|
||||
*/
|
||||
setAbsorbEditDestination(
|
||||
fileIdx: number,
|
||||
absorbEditId: AbsorbEditId,
|
||||
commitRev: Rev,
|
||||
): CommitStackState {
|
||||
assert(this.hasPendingAbsorb(), 'stack is not prepared for absorb');
|
||||
const fileStack = nullthrows(this.fileStacks.get(fileIdx));
|
||||
const edit = nullthrows(this.absorbExtra.get(fileIdx)?.get(absorbEditId));
|
||||
const selectedFileRev = edit.selectedRev;
|
||||
if (selectedFileRev != null) {
|
||||
const currentCommitRev = this.fileToCommit.get(
|
||||
FileIdx({fileIdx, fileRev: selectedFileRev}),
|
||||
)?.rev;
|
||||
if (currentCommitRev === commitRev) {
|
||||
// No need to edit.
|
||||
return this;
|
||||
}
|
||||
}
|
||||
// Figure out the "file rev" from "commit rev", since we don't know the
|
||||
// "path" of the file at the "commitRev", for now, we just naively looks up
|
||||
// the fileRev one by one... for now
|
||||
for (
|
||||
let fileRev = Math.max(1, edit.introductionRev);
|
||||
fileRev < fileStack.revLength;
|
||||
++fileRev
|
||||
) {
|
||||
if (this.fileToCommit.get(FileIdx({fileIdx, fileRev}))?.rev === commitRev) {
|
||||
// Update linelog to move the edit to "fileRev".
|
||||
const newFileRev = embedAbsorbId(fileRev, absorbEditId);
|
||||
const newFileStack = fileStack.remapRevs(rev =>
|
||||
!Number.isInteger(rev) && extractRevAbsorbId(rev)[1] === absorbEditId ? newFileRev : rev,
|
||||
);
|
||||
// Update the absorb extra too.
|
||||
const newEdit = edit.set('selectedRev', fileRev);
|
||||
const newAbsorbExtra = this.absorbExtra.setIn([fileIdx, absorbEditId], newEdit);
|
||||
// It's possible that "wdir()" is all absorbed, the new stack is
|
||||
// shorter than the original stack. So we bypass the length check.
|
||||
const newStack = this.setFileStackInternal(fileIdx, newFileStack).set(
|
||||
'absorbExtra',
|
||||
newAbsorbExtra,
|
||||
);
|
||||
return newStack;
|
||||
}
|
||||
}
|
||||
throw new Error('setAbsorbIntoRev did not find corresponding commit to absorb');
|
||||
}
|
||||
|
||||
// }}} (absorb related)
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user