rebase-git 0.0.2-nightly.20260928.132 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/runtime.js +1156 -368
- package/dist/web/assets/IconCheck-CR2g6WMA.js +1 -0
- package/dist/web/assets/IconChevronRight-Ct8_bzuc.js +14 -0
- package/dist/web/assets/IconChevronUp-C_TmKquO.js +1 -0
- package/dist/web/assets/change-diff-viewer-dGj-oUGX.js +1 -0
- package/dist/web/assets/commit-inspection-DWWpkIUb.js +4 -0
- package/dist/web/assets/commit-inspection.contract-3uL8JUQ0.js +1 -0
- package/dist/web/assets/copy-pill-C_VGk1Pg.js +1 -0
- package/dist/web/assets/error-notification-DsQIwzII.js +1 -0
- package/dist/web/assets/esm-CvNZ5U7-.js +1 -0
- package/dist/web/assets/history-worker-DH9-r88J.js +14 -0
- package/dist/web/assets/index-DMxGcYbd.js +15 -0
- package/dist/web/assets/index-DXxfLBIU.css +2 -0
- package/dist/web/assets/operation-controls-DTfubyMj.js +1 -0
- package/dist/web/assets/rebase-actions-B2Wif_Wx.js +1 -0
- package/dist/web/assets/rebase-panel-BklGSF3U.js +7 -0
- package/dist/web/assets/reflog-panel-b1hVOEPG.js +1 -0
- package/dist/web/assets/repository-changes.contract-BbNQSKl4.js +1 -0
- package/dist/web/assets/{use-diff-preferences-DOAXsFPX.js → use-diff-preferences-B8VbF4jc.js} +1 -1
- package/dist/web/assets/use-operation-CQUl8xaq.js +1 -0
- package/dist/web/assets/useToastManager-B9e_QaKk.js +1 -0
- package/dist/web/assets/whole-file-menu-pxn9TLHi.js +4 -0
- package/dist/web/assets/working-changes-D7-a0GdD.js +5 -0
- package/dist/web/index.html +16 -10
- package/package.json +1 -1
- package/dist/web/assets/IconChevronRight-CVa-IPob.js +0 -14
- package/dist/web/assets/IconChevronUp-akdYJ9UK.js +0 -1
- package/dist/web/assets/change-diff-viewer-DkyuNndG.js +0 -1
- package/dist/web/assets/commit-inspection-Wa83zWbE.js +0 -4
- package/dist/web/assets/copy-pill-CZN_HyCK.js +0 -1
- package/dist/web/assets/error-notification-DKNN1wDB.js +0 -1
- package/dist/web/assets/esm-suPXXIqE.js +0 -1
- package/dist/web/assets/history-worker-nldCB9TK.js +0 -14
- package/dist/web/assets/index-pRiFsdYb.js +0 -15
- package/dist/web/assets/index-vq_NKFMw.css +0 -2
- package/dist/web/assets/operation-controls-Dq4VBYEd.js +0 -4
- package/dist/web/assets/reflog-panel-INbii9Mx.js +0 -1
- package/dist/web/assets/use-operation-DKhyo1ke.js +0 -1
- package/dist/web/assets/working-changes-BMEZO_Vq.js +0 -5
package/dist/runtime.js
CHANGED
|
@@ -5,7 +5,7 @@ import { spawn as spawn2 } from "node:child_process";
|
|
|
5
5
|
import { existsSync as existsSync2, readFileSync, realpathSync as realpathSync2 } from "node:fs";
|
|
6
6
|
import { homedir as homedir2, release } from "node:os";
|
|
7
7
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
8
|
-
import { Deferred as Deferred3, Effect as
|
|
8
|
+
import { Deferred as Deferred3, Effect as Effect65 } from "effect";
|
|
9
9
|
|
|
10
10
|
// src/contracts/environment-connection/environment-rpc.contract.ts
|
|
11
11
|
import { Schema as Schema20 } from "effect";
|
|
@@ -575,14 +575,40 @@ var OperationScope = Schema13.Struct({
|
|
|
575
575
|
}), OperationAction = Schema13.Literals(["continue", "skip", "abort"]), OperationKind = Schema13.Literals([
|
|
576
576
|
"idle",
|
|
577
577
|
"merge",
|
|
578
|
+
"squash",
|
|
578
579
|
"rebase",
|
|
579
580
|
"cherry-pick",
|
|
580
581
|
"revert",
|
|
581
582
|
"am",
|
|
582
583
|
"unknown"
|
|
583
|
-
]),
|
|
584
|
+
]), PlanAction = Schema13.Literals([
|
|
585
|
+
"pick",
|
|
586
|
+
"reword",
|
|
587
|
+
"edit",
|
|
588
|
+
"squash",
|
|
589
|
+
"fixup",
|
|
590
|
+
"drop"
|
|
591
|
+
]), PlanStep = Schema13.Struct({
|
|
592
|
+
commit: ObjectId,
|
|
593
|
+
action: PlanAction,
|
|
594
|
+
message: Schema13.NullOr(
|
|
595
|
+
Schema13.String.check(Schema13.isMinLength(1), Schema13.isMaxLength(32e3))
|
|
596
|
+
)
|
|
597
|
+
}), RebaseStep = Schema13.Struct({
|
|
598
|
+
commit: ObjectId,
|
|
599
|
+
action: PlanAction,
|
|
600
|
+
subject: Schema13.String,
|
|
601
|
+
done: Schema13.Boolean
|
|
602
|
+
}), RepositoryOperation = Schema13.Struct({
|
|
584
603
|
kind: OperationKind,
|
|
585
|
-
phase: Schema13.Literals([
|
|
604
|
+
phase: Schema13.Literals([
|
|
605
|
+
"idle",
|
|
606
|
+
"conflicts",
|
|
607
|
+
"edit",
|
|
608
|
+
"ready",
|
|
609
|
+
"empty",
|
|
610
|
+
"blocked"
|
|
611
|
+
]),
|
|
586
612
|
revision: Schema13.String,
|
|
587
613
|
branch: Schema13.NullOr(Schema13.String),
|
|
588
614
|
commit: Schema13.NullOr(Schema13.String),
|
|
@@ -598,20 +624,79 @@ var OperationScope = Schema13.Struct({
|
|
|
598
624
|
reason: Schema13.NullOr(Schema13.String)
|
|
599
625
|
})
|
|
600
626
|
),
|
|
601
|
-
lock: Schema13.NullOr(Schema13.String)
|
|
627
|
+
lock: Schema13.NullOr(Schema13.String),
|
|
628
|
+
steps: Schema13.NullOr(Schema13.Array(RebaseStep))
|
|
602
629
|
}), ExecuteOperation = Schema13.Struct({
|
|
603
630
|
...OperationScope.fields,
|
|
604
631
|
revision: Schema13.String.check(Schema13.isPattern(/^[a-f0-9]{64}$/)),
|
|
605
632
|
action: OperationAction
|
|
633
|
+
}), MergeMode = Schema13.Literals([
|
|
634
|
+
"merge",
|
|
635
|
+
"ff-only",
|
|
636
|
+
"no-ff",
|
|
637
|
+
"squash"
|
|
638
|
+
]), StartMerge = Schema13.TaggedStruct("Merge", {
|
|
639
|
+
source: Schema13.Struct({ ref: Schema13.NullOr(RefName), commit: ObjectId }),
|
|
640
|
+
mode: MergeMode
|
|
641
|
+
}), StartRebase = Schema13.TaggedStruct("Rebase", {
|
|
642
|
+
onto: Schema13.Struct({ ref: Schema13.NullOr(RefName), commit: ObjectId }),
|
|
643
|
+
stash: Schema13.Boolean,
|
|
644
|
+
plan: Schema13.optionalKey(
|
|
645
|
+
Schema13.Array(PlanStep).check(
|
|
646
|
+
Schema13.isMinLength(1),
|
|
647
|
+
Schema13.isMaxLength(1e3)
|
|
648
|
+
)
|
|
649
|
+
)
|
|
650
|
+
}), StartRevert = Schema13.TaggedStruct("Revert", {
|
|
651
|
+
commits: Schema13.Array(ObjectId).check(
|
|
652
|
+
Schema13.isMinLength(1),
|
|
653
|
+
Schema13.isMaxLength(256)
|
|
654
|
+
),
|
|
655
|
+
commit: Schema13.Boolean
|
|
656
|
+
}), CherryPickResult = Schema13.Literals(["commit", "stage"]), StartCherryPick = Schema13.TaggedStruct("CherryPick", {
|
|
657
|
+
commits: Schema13.Array(ObjectId).check(
|
|
658
|
+
Schema13.isMinLength(1),
|
|
659
|
+
Schema13.isMaxLength(1e3)
|
|
660
|
+
),
|
|
661
|
+
mainline: Schema13.NullOr(
|
|
662
|
+
Schema13.Int.check(Schema13.isBetween({ minimum: 1, maximum: 64 }))
|
|
663
|
+
),
|
|
664
|
+
result: CherryPickResult
|
|
665
|
+
}), StartOperation = Schema13.Struct({
|
|
666
|
+
...OperationScope.fields,
|
|
667
|
+
expectedHead: ObjectId,
|
|
668
|
+
operation: Schema13.Union([
|
|
669
|
+
StartMerge,
|
|
670
|
+
StartRebase,
|
|
671
|
+
StartRevert,
|
|
672
|
+
StartCherryPick
|
|
673
|
+
])
|
|
674
|
+
}), OperationStarted = Schema13.Struct({
|
|
675
|
+
outcome: Schema13.Literals([
|
|
676
|
+
"UpToDate",
|
|
677
|
+
"FastForwarded",
|
|
678
|
+
"Committed",
|
|
679
|
+
"Rebased",
|
|
680
|
+
"Staged",
|
|
681
|
+
"Stopped"
|
|
682
|
+
]),
|
|
683
|
+
operation: RepositoryOperation
|
|
606
684
|
}), OperationFailure = Schema13.TaggedStruct("OperationFailed", {
|
|
607
685
|
reason: Schema13.Literals([
|
|
608
686
|
"Stale",
|
|
609
687
|
"Incompatible",
|
|
610
688
|
"HookFailed",
|
|
611
689
|
"GitRejected",
|
|
612
|
-
"Uncertain"
|
|
690
|
+
"Uncertain",
|
|
691
|
+
"NotFastForward",
|
|
692
|
+
"Unrelated",
|
|
693
|
+
"WouldOverwrite",
|
|
694
|
+
"Empty"
|
|
613
695
|
]),
|
|
614
|
-
detail: Schema13.String.check(Schema13.isMaxLength(2048))
|
|
696
|
+
detail: Schema13.String.check(Schema13.isMaxLength(2048)),
|
|
697
|
+
paths: Schema13.optionalKey(
|
|
698
|
+
Schema13.Array(RepositoryPath).check(Schema13.isMaxLength(100))
|
|
699
|
+
)
|
|
615
700
|
}), RepositoryOperationsApi = {
|
|
616
701
|
read: repositoryQuery("repositories/operations/read", {
|
|
617
702
|
request: OperationScope,
|
|
@@ -621,6 +706,11 @@ var OperationScope = Schema13.Struct({
|
|
|
621
706
|
request: ExecuteOperation,
|
|
622
707
|
success: RepositoryOperation,
|
|
623
708
|
failure: OperationFailure
|
|
709
|
+
}),
|
|
710
|
+
start: repositoryCommand("repositories/operations/start", {
|
|
711
|
+
request: StartOperation,
|
|
712
|
+
success: OperationStarted,
|
|
713
|
+
failure: OperationFailure
|
|
624
714
|
})
|
|
625
715
|
};
|
|
626
716
|
|
|
@@ -710,9 +800,22 @@ var PushDestination = Schema15.Struct({
|
|
|
710
800
|
}), RemoteBranchUpdated = Schema15.Struct({
|
|
711
801
|
destination: PushDestination,
|
|
712
802
|
target: ObjectId
|
|
803
|
+
}), PushTags = Schema15.Struct({
|
|
804
|
+
repositoryId: RepositoryId,
|
|
805
|
+
worktreePath: RepositoryPath,
|
|
806
|
+
remote: RemoteName,
|
|
807
|
+
tags: Schema15.Array(RefName).check(
|
|
808
|
+
Schema15.isMinLength(1),
|
|
809
|
+
Schema15.isMaxLength(100)
|
|
810
|
+
)
|
|
811
|
+
}), TagsPushed = Schema15.Struct({
|
|
812
|
+
remote: RemoteName,
|
|
813
|
+
pushed: Schema15.Array(RefName),
|
|
814
|
+
upToDate: Schema15.Array(RefName)
|
|
713
815
|
}), PushRejectedReason = Schema15.Literals([
|
|
714
816
|
"RemoteMissing",
|
|
715
817
|
"InvalidBranch",
|
|
818
|
+
"TagExists",
|
|
716
819
|
"NonFastForward",
|
|
717
820
|
"LeaseRejected",
|
|
718
821
|
"HookDeclined",
|
|
@@ -727,6 +830,11 @@ var PushDestination = Schema15.Struct({
|
|
|
727
830
|
request: PushBranch,
|
|
728
831
|
success: RemoteBranchUpdated,
|
|
729
832
|
failure: PushRejected
|
|
833
|
+
}),
|
|
834
|
+
pushTags: repositoryCommand("repositories/push/tags", {
|
|
835
|
+
request: PushTags,
|
|
836
|
+
success: TagsPushed,
|
|
837
|
+
failure: PushRejected
|
|
730
838
|
})
|
|
731
839
|
};
|
|
732
840
|
|
|
@@ -735,7 +843,7 @@ import { Schema as Schema17 } from "effect";
|
|
|
735
843
|
|
|
736
844
|
// src/contracts/repository-refs/repository-refs.contract.ts
|
|
737
845
|
import { Schema as Schema16 } from "effect";
|
|
738
|
-
var
|
|
846
|
+
var RepositoryHead = Schema16.Struct({
|
|
739
847
|
branch: Schema16.optional(RefName),
|
|
740
848
|
commit: ObjectId
|
|
741
849
|
}), RepositoryWorktree = Schema16.Struct({
|
|
@@ -761,7 +869,8 @@ var FailureDetail = Schema16.String.check(Schema16.isMaxLength(2048)), Repositor
|
|
|
761
869
|
remote: RemoteName
|
|
762
870
|
}), RepositoryTag = Schema16.Struct({
|
|
763
871
|
name: RefName,
|
|
764
|
-
target: Schema16.optional(ObjectId)
|
|
872
|
+
target: Schema16.optional(ObjectId),
|
|
873
|
+
object: Schema16.optional(ObjectId)
|
|
765
874
|
}), RepositoryRefsTruncation = Schema16.Struct({
|
|
766
875
|
branches: Schema16.Boolean,
|
|
767
876
|
remoteBranches: Schema16.Boolean,
|
|
@@ -822,7 +931,6 @@ var FailureDetail = Schema16.String.check(Schema16.isMaxLength(2048)), Repositor
|
|
|
822
931
|
worktreePath: RepositoryPath
|
|
823
932
|
}
|
|
824
933
|
), CheckoutRejected = Schema16.TaggedStruct("CheckoutRejected", {
|
|
825
|
-
detail: FailureDetail,
|
|
826
934
|
reason: Schema16.Literals(["LocalChanges", "StashFailed"])
|
|
827
935
|
}), RepositoryCheckoutFailure = Schema16.Union([
|
|
828
936
|
RefMissing,
|
|
@@ -995,18 +1103,44 @@ import { Schema as Schema19 } from "effect";
|
|
|
995
1103
|
var TagScope = {
|
|
996
1104
|
repositoryId: RepositoryId,
|
|
997
1105
|
worktreePath: RepositoryPath
|
|
998
|
-
},
|
|
1106
|
+
}, TagMessage = Schema19.String.check(
|
|
1107
|
+
Schema19.isMinLength(1),
|
|
1108
|
+
Schema19.isMaxLength(65536)
|
|
1109
|
+
), CreateRepositoryTag = Schema19.Struct({
|
|
999
1110
|
...TagScope,
|
|
1000
1111
|
name: RefName,
|
|
1001
|
-
target: ObjectId
|
|
1112
|
+
target: ObjectId,
|
|
1113
|
+
message: Schema19.optional(TagMessage)
|
|
1002
1114
|
}), DeleteRepositoryTag = Schema19.Struct({
|
|
1003
1115
|
...TagScope,
|
|
1004
|
-
name: RefName
|
|
1116
|
+
name: RefName,
|
|
1117
|
+
local: Schema19.optional(Schema19.Struct({ object: ObjectId })),
|
|
1118
|
+
remote: Schema19.optional(
|
|
1119
|
+
Schema19.Struct({ remote: RemoteName, object: ObjectId })
|
|
1120
|
+
)
|
|
1005
1121
|
}), RepositoryTagDeleted = Schema19.Struct({
|
|
1122
|
+
name: RefName
|
|
1123
|
+
}), ReadRepositoryTag = Schema19.Struct({
|
|
1124
|
+
...TagScope,
|
|
1125
|
+
name: RefName
|
|
1126
|
+
}), RepositoryTagAnnotation = Schema19.Struct({
|
|
1006
1127
|
name: RefName,
|
|
1007
|
-
|
|
1128
|
+
object: ObjectId,
|
|
1129
|
+
target: ObjectId,
|
|
1130
|
+
tagger: Schema19.Struct({
|
|
1131
|
+
name: Schema19.String.check(Schema19.isMaxLength(512)),
|
|
1132
|
+
date: Schema19.String.check(Schema19.isMaxLength(64))
|
|
1133
|
+
}),
|
|
1134
|
+
message: Schema19.String.check(Schema19.isMaxLength(65536)),
|
|
1135
|
+
signed: Schema19.Boolean
|
|
1008
1136
|
}), TagRejected = Schema19.TaggedStruct("TagRejected", {
|
|
1009
|
-
reason: Schema19.Literals([
|
|
1137
|
+
reason: Schema19.Literals([
|
|
1138
|
+
"InvalidName",
|
|
1139
|
+
"Exists",
|
|
1140
|
+
"MessageRequired",
|
|
1141
|
+
"Moved",
|
|
1142
|
+
"RemoteDiffers"
|
|
1143
|
+
])
|
|
1010
1144
|
}), RepositoryTagsApi = {
|
|
1011
1145
|
create: repositoryCommand("repositories/tags/create", {
|
|
1012
1146
|
request: CreateRepositoryTag,
|
|
@@ -1016,6 +1150,11 @@ var TagScope = {
|
|
|
1016
1150
|
delete: repositoryCommand("repositories/tags/delete", {
|
|
1017
1151
|
request: DeleteRepositoryTag,
|
|
1018
1152
|
success: RepositoryTagDeleted,
|
|
1153
|
+
failure: Schema19.Union([RefMissing, TagRejected])
|
|
1154
|
+
}),
|
|
1155
|
+
annotation: repositoryQuery("repositories/tags/annotation", {
|
|
1156
|
+
request: ReadRepositoryTag,
|
|
1157
|
+
success: RepositoryTagAnnotation,
|
|
1019
1158
|
failure: RefMissing
|
|
1020
1159
|
})
|
|
1021
1160
|
};
|
|
@@ -1111,7 +1250,7 @@ function octets(address) {
|
|
|
1111
1250
|
}
|
|
1112
1251
|
|
|
1113
1252
|
// src/apps/server/app/server/start-environment-server.ts
|
|
1114
|
-
import { Effect as
|
|
1253
|
+
import { Effect as Effect64 } from "effect";
|
|
1115
1254
|
|
|
1116
1255
|
// src/apps/server/adapters/local-git/git-commands.ts
|
|
1117
1256
|
import {
|
|
@@ -1280,8 +1419,10 @@ function gitArguments(command) {
|
|
|
1280
1419
|
];
|
|
1281
1420
|
}
|
|
1282
1421
|
function gitEnvironment(command) {
|
|
1422
|
+
let { GIT_SEQUENCE_EDITOR: _, ...inherited } = process.env;
|
|
1283
1423
|
return {
|
|
1284
|
-
...
|
|
1424
|
+
...inherited,
|
|
1425
|
+
...command.environment,
|
|
1285
1426
|
GIT_EDITOR: "true",
|
|
1286
1427
|
GIT_OPTIONAL_LOCKS: "0",
|
|
1287
1428
|
GIT_TERMINAL_PROMPT: "0",
|
|
@@ -1371,9 +1512,9 @@ function compareVersions(left, right) {
|
|
|
1371
1512
|
}
|
|
1372
1513
|
|
|
1373
1514
|
// src/apps/server/app/server/serve-environment.ts
|
|
1374
|
-
import { join as
|
|
1515
|
+
import { join as join12 } from "node:path";
|
|
1375
1516
|
import { and, eq as eq4, isNull } from "drizzle-orm";
|
|
1376
|
-
import { Effect as
|
|
1517
|
+
import { Effect as Effect63 } from "effect";
|
|
1377
1518
|
|
|
1378
1519
|
// src/apps/server/adapters/environment-transport/environment-event-publisher.ts
|
|
1379
1520
|
function createEnvironmentEventPublisher() {
|
|
@@ -2173,6 +2314,11 @@ function createEnvironmentHttpHandler(authorization, runEnvironmentEffect, brows
|
|
|
2173
2314
|
Effect10.catch(
|
|
2174
2315
|
(error) => Effect10.sync(() => writeEnvironmentHttpError(response, error))
|
|
2175
2316
|
),
|
|
2317
|
+
Effect10.catchCause(
|
|
2318
|
+
() => Effect10.sync(() => {
|
|
2319
|
+
response.headersSent || response.writeHead(500).end();
|
|
2320
|
+
})
|
|
2321
|
+
),
|
|
2176
2322
|
Effect10.ensuring(Effect10.sync(() => response.off("close", abort)))
|
|
2177
2323
|
),
|
|
2178
2324
|
abortController.signal
|
|
@@ -4742,7 +4888,7 @@ function readConflictSides(git, directory, operation) {
|
|
|
4742
4888
|
});
|
|
4743
4889
|
}
|
|
4744
4890
|
function baseCommit(git, directory, kind, incoming) {
|
|
4745
|
-
return incoming === null ? Effect28.succeed(null) : kind === "merge" ? firstLine(
|
|
4891
|
+
return incoming === null ? Effect28.succeed(null) : kind === "merge" || kind === "squash" ? firstLine(
|
|
4746
4892
|
runRepositoryGit(git, directory, ["merge-base", "HEAD", incoming], {
|
|
4747
4893
|
exitCodes: [0, 1]
|
|
4748
4894
|
})
|
|
@@ -5410,7 +5556,459 @@ function sameOids(left, right) {
|
|
|
5410
5556
|
}
|
|
5411
5557
|
|
|
5412
5558
|
// src/apps/server/features/repository-operations/repository-operations.ts
|
|
5559
|
+
import { Effect as Effect40 } from "effect";
|
|
5560
|
+
|
|
5561
|
+
// src/apps/server/features/repository-operations/cherry-pick.ts
|
|
5562
|
+
import { readFile as readFile4 } from "node:fs/promises";
|
|
5563
|
+
import { join as join8, resolve as resolve4 } from "node:path";
|
|
5564
|
+
import { Effect as Effect37 } from "effect";
|
|
5565
|
+
|
|
5566
|
+
// src/apps/server/features/repository-operations/operation-outcome.ts
|
|
5413
5567
|
import { Effect as Effect36 } from "effect";
|
|
5568
|
+
function started(outcome, operation) {
|
|
5569
|
+
return { outcome, operation };
|
|
5570
|
+
}
|
|
5571
|
+
function readCommit(git, directory, rev) {
|
|
5572
|
+
return runRepositoryGit(
|
|
5573
|
+
git,
|
|
5574
|
+
directory,
|
|
5575
|
+
["rev-parse", "--verify", "--quiet", "--end-of-options", `${rev}^{commit}`],
|
|
5576
|
+
{ exitCodes: [0, 1] }
|
|
5577
|
+
).pipe(Effect36.map((output) => output.trim()));
|
|
5578
|
+
}
|
|
5579
|
+
function requireGitSuccess(output) {
|
|
5580
|
+
if (output.exitCode === 0) return Effect36.void;
|
|
5581
|
+
let detail = output.stderr || output.stdout || "Git rejected the action. Check the worktree and configured hooks.";
|
|
5582
|
+
return /\.lock['\s:]|another git process/i.test(detail) ? Effect36.fail(repositoryRejected("Busy", detail)) : /would be overwritten by merge/i.test(detail) ? Effect36.fail({
|
|
5583
|
+
...operationError("WouldOverwrite", detail),
|
|
5584
|
+
paths: detail.split(`
|
|
5585
|
+
`).filter((line) => line.startsWith(" ")).map((line) => line.trim()).filter((path) => path.length > 0).slice(0, 100)
|
|
5586
|
+
}) : Effect36.fail(
|
|
5587
|
+
operationError(
|
|
5588
|
+
/hook|pre-commit|commit-msg|pre-rebase|not committing merge/i.test(detail) ? "HookFailed" : "GitRejected",
|
|
5589
|
+
detail
|
|
5590
|
+
)
|
|
5591
|
+
);
|
|
5592
|
+
}
|
|
5593
|
+
function uncertain() {
|
|
5594
|
+
return operationError(
|
|
5595
|
+
"Uncertain",
|
|
5596
|
+
"Git's result could not be confirmed. Refresh the worktree before trying again."
|
|
5597
|
+
);
|
|
5598
|
+
}
|
|
5599
|
+
function operationFailure(reason, detail) {
|
|
5600
|
+
return Effect36.fail(operationError(reason, detail));
|
|
5601
|
+
}
|
|
5602
|
+
function operationError(reason, detail) {
|
|
5603
|
+
return { _tag: "OperationFailed", reason, detail: detail.slice(0, 2048) };
|
|
5604
|
+
}
|
|
5605
|
+
|
|
5606
|
+
// src/apps/server/features/repository-operations/cherry-pick.ts
|
|
5607
|
+
function startCherryPick(git, coordination, { worktreePath: directory, expectedHead }, { commits, mainline, result }) {
|
|
5608
|
+
return Effect37.gen(function* () {
|
|
5609
|
+
if ((yield* readCommit(git, directory, "HEAD")) !== expectedHead)
|
|
5610
|
+
return yield* operationFailure("Stale", "HEAD moved. Try again.");
|
|
5611
|
+
let merges = (yield* readParentCounts(git, directory, commits)).filter(
|
|
5612
|
+
(count) => count > 1
|
|
5613
|
+
);
|
|
5614
|
+
if (merges.length > 1)
|
|
5615
|
+
return yield* operationFailure(
|
|
5616
|
+
"Incompatible",
|
|
5617
|
+
"Cherry-pick one merge commit at a time."
|
|
5618
|
+
);
|
|
5619
|
+
if (merges.length === 1 && mainline === null)
|
|
5620
|
+
return yield* operationFailure(
|
|
5621
|
+
"Incompatible",
|
|
5622
|
+
"Choose a parent for the merge commit."
|
|
5623
|
+
);
|
|
5624
|
+
if (mainline !== null && merges.some((count) => mainline > count))
|
|
5625
|
+
return yield* operationFailure(
|
|
5626
|
+
"Incompatible",
|
|
5627
|
+
`The merge commit has no parent ${mainline}.`
|
|
5628
|
+
);
|
|
5629
|
+
if (result === "stage" && (yield* hasStagedChanges(git, directory)))
|
|
5630
|
+
return yield* operationFailure(
|
|
5631
|
+
"Incompatible",
|
|
5632
|
+
"Commit or unstage the staged changes first."
|
|
5633
|
+
);
|
|
5634
|
+
let output = yield* git.run({
|
|
5635
|
+
directory,
|
|
5636
|
+
arguments: [
|
|
5637
|
+
"cherry-pick",
|
|
5638
|
+
...result === "stage" ? ["--no-commit"] : [],
|
|
5639
|
+
...mainline === null ? [] : ["-m", String(mainline)],
|
|
5640
|
+
"--end-of-options",
|
|
5641
|
+
...commits
|
|
5642
|
+
],
|
|
5643
|
+
timeoutMilliseconds: 12e4
|
|
5644
|
+
}).pipe(Effect37.mapError(uncertain), Effect37.uninterruptible), state = yield* coordination.operation(directory);
|
|
5645
|
+
return output.exitCode !== 0 && (state.kind === "cherry-pick" || result === "stage" && (yield* hasUnmergedPaths(git, directory))) ? started("Stopped", state) : (yield* requireGitSuccess(output), started(result === "stage" ? "Staged" : "Committed", state));
|
|
5646
|
+
});
|
|
5647
|
+
}
|
|
5648
|
+
function continueStagedCherryPick(git, directory) {
|
|
5649
|
+
return Effect37.gen(function* () {
|
|
5650
|
+
let sequencer = yield* readStagedSequencer(git, directory);
|
|
5651
|
+
if (sequencer === void 0) return !1;
|
|
5652
|
+
if (yield* runRepositoryGit(git, directory, ["cherry-pick", "--quit"]), sequencer.remaining.length === 0) return !0;
|
|
5653
|
+
let output = yield* git.run({
|
|
5654
|
+
directory,
|
|
5655
|
+
arguments: [
|
|
5656
|
+
"cherry-pick",
|
|
5657
|
+
"--no-commit",
|
|
5658
|
+
...sequencer.mainline === null ? [] : ["-m", sequencer.mainline],
|
|
5659
|
+
"--end-of-options",
|
|
5660
|
+
...sequencer.remaining
|
|
5661
|
+
],
|
|
5662
|
+
timeoutMilliseconds: 12e4
|
|
5663
|
+
}).pipe(Effect37.mapError(uncertain));
|
|
5664
|
+
return output.exitCode !== 0 && !(yield* hasUnmergedPaths(git, directory)) && (yield* requireGitSuccess(output)), !0;
|
|
5665
|
+
});
|
|
5666
|
+
}
|
|
5667
|
+
function readStagedSequencer(git, directory) {
|
|
5668
|
+
return Effect37.gen(function* () {
|
|
5669
|
+
let path = resolve4(
|
|
5670
|
+
directory,
|
|
5671
|
+
(yield* runRepositoryGit(git, directory, [
|
|
5672
|
+
"rev-parse",
|
|
5673
|
+
"--git-path",
|
|
5674
|
+
"sequencer"
|
|
5675
|
+
])).trim()
|
|
5676
|
+
), [options, todo] = yield* Effect37.tryPromise({
|
|
5677
|
+
try: () => Promise.all([
|
|
5678
|
+
readFile4(join8(path, "opts"), "utf8"),
|
|
5679
|
+
readFile4(join8(path, "todo"), "utf8")
|
|
5680
|
+
]),
|
|
5681
|
+
catch: () => {
|
|
5682
|
+
}
|
|
5683
|
+
}).pipe(Effect37.orElseSucceed(() => [null, null]));
|
|
5684
|
+
if (!(options === null || todo === null) && /no-commit\s*=\s*true/.test(options))
|
|
5685
|
+
return {
|
|
5686
|
+
mainline: /mainline\s*=\s*(\d+)/.exec(options)?.[1] ?? null,
|
|
5687
|
+
remaining: todo.split(`
|
|
5688
|
+
`).flatMap((line) => /^(?:pick|p)\s+(\S+)/.exec(line)?.[1] ?? []).slice(1)
|
|
5689
|
+
};
|
|
5690
|
+
});
|
|
5691
|
+
}
|
|
5692
|
+
function readParentCounts(git, directory, commits) {
|
|
5693
|
+
return runRepositoryGit(git, directory, [
|
|
5694
|
+
"rev-list",
|
|
5695
|
+
"--no-walk=unsorted",
|
|
5696
|
+
"--parents",
|
|
5697
|
+
"--end-of-options",
|
|
5698
|
+
...commits
|
|
5699
|
+
]).pipe(
|
|
5700
|
+
Effect37.catchTag(
|
|
5701
|
+
"GitFailed",
|
|
5702
|
+
() => operationFailure(
|
|
5703
|
+
"Incompatible",
|
|
5704
|
+
"A selected commit is not in this repository."
|
|
5705
|
+
)
|
|
5706
|
+
),
|
|
5707
|
+
Effect37.map(
|
|
5708
|
+
(output) => output.split(`
|
|
5709
|
+
`).filter((line) => line.length > 0).map((line) => line.split(" ").length - 1)
|
|
5710
|
+
)
|
|
5711
|
+
);
|
|
5712
|
+
}
|
|
5713
|
+
function hasStagedChanges(git, directory) {
|
|
5714
|
+
return runRepositoryGitOutput(
|
|
5715
|
+
git,
|
|
5716
|
+
directory,
|
|
5717
|
+
["diff", "--cached", "--quiet"],
|
|
5718
|
+
{ exitCodes: [0, 1] }
|
|
5719
|
+
).pipe(Effect37.map(({ exitCode }) => exitCode === 1));
|
|
5720
|
+
}
|
|
5721
|
+
function hasUnmergedPaths(git, directory) {
|
|
5722
|
+
return runRepositoryGit(git, directory, ["ls-files", "--unmerged"]).pipe(
|
|
5723
|
+
Effect37.map((unmerged) => unmerged.length > 0)
|
|
5724
|
+
);
|
|
5725
|
+
}
|
|
5726
|
+
|
|
5727
|
+
// src/apps/server/features/repository-operations/start-operation.ts
|
|
5728
|
+
import { Effect as Effect39 } from "effect";
|
|
5729
|
+
|
|
5730
|
+
// src/apps/server/features/repository-operations/rebase-plan.ts
|
|
5731
|
+
import { Effect as Effect38 } from "effect";
|
|
5732
|
+
var todoCommands = {
|
|
5733
|
+
pick: "pick",
|
|
5734
|
+
reword: "pick",
|
|
5735
|
+
edit: "edit",
|
|
5736
|
+
squash: "fixup",
|
|
5737
|
+
fixup: "fixup",
|
|
5738
|
+
drop: "drop"
|
|
5739
|
+
};
|
|
5740
|
+
function rebasePlanTodo(git, directory, range, plan) {
|
|
5741
|
+
return Effect38.gen(function* () {
|
|
5742
|
+
let replayed = new Map(
|
|
5743
|
+
(yield* runRepositoryGit(git, directory, [
|
|
5744
|
+
"log",
|
|
5745
|
+
"--no-merges",
|
|
5746
|
+
"--format=%H%x00%P%x00%an%x00%ae%x00%ad%x00%s",
|
|
5747
|
+
"--date=raw",
|
|
5748
|
+
range
|
|
5749
|
+
])).split(`
|
|
5750
|
+
`).filter(Boolean).map((line) => {
|
|
5751
|
+
let [commit = "", parents = "", name, email, date, subject] = line.split("\0");
|
|
5752
|
+
return [
|
|
5753
|
+
commit,
|
|
5754
|
+
{ parent: parents.split(" ")[0] ?? "", name, email, date, subject }
|
|
5755
|
+
];
|
|
5756
|
+
})
|
|
5757
|
+
);
|
|
5758
|
+
if (new Set(plan.map((step) => step.commit)).size !== plan.length || replayed.size !== plan.length || plan.some((step) => !replayed.has(step.commit)))
|
|
5759
|
+
return yield* operationFailure(
|
|
5760
|
+
"Stale",
|
|
5761
|
+
"The branch changed since the plan was made. Try again."
|
|
5762
|
+
);
|
|
5763
|
+
let problem = planProblem(plan);
|
|
5764
|
+
if (problem !== void 0)
|
|
5765
|
+
return yield* operationFailure("Incompatible", problem);
|
|
5766
|
+
let signed = (yield* runRepositoryGit(
|
|
5767
|
+
git,
|
|
5768
|
+
directory,
|
|
5769
|
+
["config", "--type=bool", "--get", "commit.gpgSign"],
|
|
5770
|
+
{ exitCodes: [0, 1] }
|
|
5771
|
+
)).trim() === "true", lines = [];
|
|
5772
|
+
for (let step of plan) {
|
|
5773
|
+
let original = replayed.get(step.commit), commit = step.message === null || original === void 0 ? step.commit : (yield* runRepositoryGit(
|
|
5774
|
+
git,
|
|
5775
|
+
directory,
|
|
5776
|
+
[
|
|
5777
|
+
"commit-tree",
|
|
5778
|
+
signed ? "-S" : "--no-gpg-sign",
|
|
5779
|
+
`${step.commit}^{tree}`,
|
|
5780
|
+
...original.parent === "" ? [] : ["-p", original.parent]
|
|
5781
|
+
],
|
|
5782
|
+
{
|
|
5783
|
+
input: `${step.message}
|
|
5784
|
+
`,
|
|
5785
|
+
environment: {
|
|
5786
|
+
GIT_AUTHOR_NAME: original.name ?? "",
|
|
5787
|
+
GIT_AUTHOR_EMAIL: original.email ?? "",
|
|
5788
|
+
GIT_AUTHOR_DATE: `@${original.date ?? ""}`
|
|
5789
|
+
}
|
|
5790
|
+
}
|
|
5791
|
+
)).trim(), subject = (step.message ?? original?.subject ?? "").split(
|
|
5792
|
+
/\r?\n/
|
|
5793
|
+
)[0];
|
|
5794
|
+
lines.push(`${todoCommands[step.action]} ${commit} # ${subject}`);
|
|
5795
|
+
}
|
|
5796
|
+
return `${lines.join(`
|
|
5797
|
+
`)}
|
|
5798
|
+
`;
|
|
5799
|
+
});
|
|
5800
|
+
}
|
|
5801
|
+
function planProblem(plan) {
|
|
5802
|
+
let kept, squashed = !1, unmatched = (head) => head !== void 0 && head.message !== null !== (head.action === "reword" || squashed);
|
|
5803
|
+
for (let step of plan) {
|
|
5804
|
+
if (step.action === "drop") {
|
|
5805
|
+
if (step.message !== null) return "A dropped commit has no message.";
|
|
5806
|
+
continue;
|
|
5807
|
+
}
|
|
5808
|
+
if (folds(step)) {
|
|
5809
|
+
if (kept === void 0)
|
|
5810
|
+
return `${step.commit.slice(0, 8)} has nothing older to fold into.`;
|
|
5811
|
+
if (step.message !== null)
|
|
5812
|
+
return "A folded commit takes the message of its group.";
|
|
5813
|
+
squashed ||= step.action === "squash";
|
|
5814
|
+
continue;
|
|
5815
|
+
}
|
|
5816
|
+
if (unmatched(kept))
|
|
5817
|
+
return `${kept?.commit.slice(0, 8)} needs exactly one message.`;
|
|
5818
|
+
kept = step, squashed = !1;
|
|
5819
|
+
}
|
|
5820
|
+
return unmatched(kept) ? `${kept?.commit.slice(0, 8)} needs exactly one message.` : void 0;
|
|
5821
|
+
}
|
|
5822
|
+
function folds(step) {
|
|
5823
|
+
return step.action === "squash" || step.action === "fixup";
|
|
5824
|
+
}
|
|
5825
|
+
|
|
5826
|
+
// src/apps/server/features/repository-operations/start-operation.ts
|
|
5827
|
+
function startOperation(git, coordination, command) {
|
|
5828
|
+
let { operation } = command;
|
|
5829
|
+
switch (operation._tag) {
|
|
5830
|
+
case "Merge":
|
|
5831
|
+
return startMerge(git, coordination, command, operation);
|
|
5832
|
+
case "Rebase":
|
|
5833
|
+
return startRebase(git, coordination, command, operation);
|
|
5834
|
+
case "Revert":
|
|
5835
|
+
return revertCommits(git, coordination, command, operation);
|
|
5836
|
+
case "CherryPick":
|
|
5837
|
+
return startCherryPick(git, coordination, command, operation);
|
|
5838
|
+
}
|
|
5839
|
+
}
|
|
5840
|
+
var mergeArguments = {
|
|
5841
|
+
merge: ["--ff", "--no-edit"],
|
|
5842
|
+
"ff-only": ["--ff-only"],
|
|
5843
|
+
"no-ff": ["--no-ff", "--no-edit"],
|
|
5844
|
+
squash: ["--squash"]
|
|
5845
|
+
};
|
|
5846
|
+
function startMerge(git, coordination, { worktreePath: directory, expectedHead }, { source, mode }) {
|
|
5847
|
+
let revision = source.ref ?? source.commit;
|
|
5848
|
+
return Effect39.gen(function* () {
|
|
5849
|
+
let head = yield* readCommit(git, directory, "HEAD");
|
|
5850
|
+
if (head !== expectedHead)
|
|
5851
|
+
return yield* operationFailure("Stale", "HEAD moved. Try again.");
|
|
5852
|
+
if (!(yield* onBranch(git, directory)))
|
|
5853
|
+
return yield* operationFailure("Incompatible", "HEAD is detached.");
|
|
5854
|
+
if ((yield* readCommit(git, directory, revision)) !== source.commit)
|
|
5855
|
+
return yield* operationFailure("Stale", `${revision} moved. Try again.`);
|
|
5856
|
+
let base = yield* runRepositoryGit(
|
|
5857
|
+
git,
|
|
5858
|
+
directory,
|
|
5859
|
+
["merge-base", head, source.commit],
|
|
5860
|
+
{ exitCodes: [0, 1] }
|
|
5861
|
+
);
|
|
5862
|
+
if (base.trim() === "")
|
|
5863
|
+
return yield* operationFailure(
|
|
5864
|
+
"Unrelated",
|
|
5865
|
+
`${revision} has no history in common with the current branch.`
|
|
5866
|
+
);
|
|
5867
|
+
if (base.trim() === source.commit)
|
|
5868
|
+
return started("UpToDate", yield* coordination.operation(directory));
|
|
5869
|
+
if (mode === "ff-only" && base.trim() !== head)
|
|
5870
|
+
return yield* operationFailure(
|
|
5871
|
+
"NotFastForward",
|
|
5872
|
+
`Can't fast-forward to ${revision}.`
|
|
5873
|
+
);
|
|
5874
|
+
let output = yield* git.run({
|
|
5875
|
+
directory,
|
|
5876
|
+
arguments: [
|
|
5877
|
+
"merge",
|
|
5878
|
+
"--no-autostash",
|
|
5879
|
+
"--no-stat",
|
|
5880
|
+
...mergeArguments[mode],
|
|
5881
|
+
"--end-of-options",
|
|
5882
|
+
revision
|
|
5883
|
+
],
|
|
5884
|
+
timeoutMilliseconds: 12e4
|
|
5885
|
+
}).pipe(Effect39.mapError(uncertain), Effect39.uninterruptible), state = yield* coordination.operation(directory);
|
|
5886
|
+
if (state.phase === "conflicts") return started("Stopped", state);
|
|
5887
|
+
if (yield* requireGitSuccess(output), mode === "squash") return started("Staged", state);
|
|
5888
|
+
let merged = yield* readCommit(git, directory, "HEAD");
|
|
5889
|
+
return started(
|
|
5890
|
+
merged === head ? "UpToDate" : merged === source.commit ? "FastForwarded" : "Committed",
|
|
5891
|
+
state
|
|
5892
|
+
);
|
|
5893
|
+
});
|
|
5894
|
+
}
|
|
5895
|
+
function startRebase(git, coordination, { worktreePath: directory, expectedHead }, { onto, stash, plan }) {
|
|
5896
|
+
let revision = onto.ref ?? onto.commit;
|
|
5897
|
+
return Effect39.gen(function* () {
|
|
5898
|
+
let [head, branch, target, changes, merges] = yield* Effect39.all(
|
|
5899
|
+
[
|
|
5900
|
+
readCommit(git, directory, "HEAD"),
|
|
5901
|
+
onBranch(git, directory),
|
|
5902
|
+
readCommit(git, directory, revision),
|
|
5903
|
+
runRepositoryGit(
|
|
5904
|
+
git,
|
|
5905
|
+
directory,
|
|
5906
|
+
["status", "--porcelain=v1", "-z", "--untracked-files=no"],
|
|
5907
|
+
{ globalArguments: ["--no-optional-locks"] }
|
|
5908
|
+
),
|
|
5909
|
+
runRepositoryGit(
|
|
5910
|
+
git,
|
|
5911
|
+
directory,
|
|
5912
|
+
[
|
|
5913
|
+
"rev-list",
|
|
5914
|
+
"--merges",
|
|
5915
|
+
"--max-count=1",
|
|
5916
|
+
`${onto.commit}..${expectedHead}`
|
|
5917
|
+
],
|
|
5918
|
+
{ exitCodes: [0, 128] }
|
|
5919
|
+
)
|
|
5920
|
+
],
|
|
5921
|
+
{ concurrency: "unbounded" }
|
|
5922
|
+
);
|
|
5923
|
+
if (head !== expectedHead)
|
|
5924
|
+
return yield* operationFailure("Stale", "HEAD moved. Try again.");
|
|
5925
|
+
if (!branch)
|
|
5926
|
+
return yield* operationFailure("Incompatible", "HEAD is detached.");
|
|
5927
|
+
if (target !== onto.commit)
|
|
5928
|
+
return yield* operationFailure("Stale", `${revision} moved. Try again.`);
|
|
5929
|
+
if (changes.length > 0 && !stash)
|
|
5930
|
+
return yield* operationFailure(
|
|
5931
|
+
"Stale",
|
|
5932
|
+
"Files changed in the worktree. Try again."
|
|
5933
|
+
);
|
|
5934
|
+
let todo = plan === void 0 ? void 0 : yield* rebasePlanTodo(
|
|
5935
|
+
git,
|
|
5936
|
+
directory,
|
|
5937
|
+
`${onto.commit}..${expectedHead}`,
|
|
5938
|
+
plan
|
|
5939
|
+
), output = yield* git.run({
|
|
5940
|
+
directory,
|
|
5941
|
+
...todo === void 0 ? {} : { globalArguments: ["-c", "sequence.editor=cat >"], input: todo },
|
|
5942
|
+
arguments: [
|
|
5943
|
+
"rebase",
|
|
5944
|
+
...todo === void 0 ? [] : ["--interactive", "--empty=drop"],
|
|
5945
|
+
merges.length > 0 && todo === void 0 ? "--rebase-merges" : "--no-rebase-merges",
|
|
5946
|
+
stash ? "--autostash" : "--no-autostash",
|
|
5947
|
+
"--end-of-options",
|
|
5948
|
+
revision
|
|
5949
|
+
],
|
|
5950
|
+
timeoutMilliseconds: 6e5
|
|
5951
|
+
}).pipe(Effect39.mapError(uncertain), Effect39.uninterruptible), state = yield* coordination.operation(directory);
|
|
5952
|
+
if (state.kind === "rebase") return started("Stopped", state);
|
|
5953
|
+
if (yield* requireGitSuccess(output), /resulted in conflicts/.test(`${output.stdout}
|
|
5954
|
+
${output.stderr}`))
|
|
5955
|
+
return yield* operationFailure(
|
|
5956
|
+
"GitRejected",
|
|
5957
|
+
`${output.stderr}${output.stdout}`
|
|
5958
|
+
);
|
|
5959
|
+
let rebased = yield* readCommit(git, directory, "HEAD");
|
|
5960
|
+
return started(
|
|
5961
|
+
rebased === head ? "UpToDate" : rebased === onto.commit ? "FastForwarded" : "Rebased",
|
|
5962
|
+
state
|
|
5963
|
+
);
|
|
5964
|
+
});
|
|
5965
|
+
}
|
|
5966
|
+
function revertCommits(git, coordination, { worktreePath: directory, expectedHead }, { commits, commit }) {
|
|
5967
|
+
return Effect39.gen(function* () {
|
|
5968
|
+
if ((yield* readCommit(git, directory, "HEAD")) !== expectedHead)
|
|
5969
|
+
return yield* operationFailure("Stale", "HEAD moved. Try again.");
|
|
5970
|
+
let outside = yield* runRepositoryGit(git, directory, [
|
|
5971
|
+
"rev-list",
|
|
5972
|
+
...commits,
|
|
5973
|
+
"--not",
|
|
5974
|
+
"HEAD"
|
|
5975
|
+
]), foreign = commits.find((oid) => outside.includes(oid));
|
|
5976
|
+
if (foreign !== void 0)
|
|
5977
|
+
return yield* operationFailure(
|
|
5978
|
+
"Incompatible",
|
|
5979
|
+
`${foreign.slice(0, 8)} is not in the checked-out branch.`
|
|
5980
|
+
);
|
|
5981
|
+
let output = yield* git.run({
|
|
5982
|
+
directory,
|
|
5983
|
+
arguments: [
|
|
5984
|
+
"revert",
|
|
5985
|
+
"--no-edit",
|
|
5986
|
+
"--mainline",
|
|
5987
|
+
"1",
|
|
5988
|
+
...commit ? [] : ["--no-commit"],
|
|
5989
|
+
...commits
|
|
5990
|
+
],
|
|
5991
|
+
timeoutMilliseconds: 12e4
|
|
5992
|
+
}).pipe(Effect39.mapError(uncertain), Effect39.uninterruptible), state = yield* coordination.operation(directory), empty = /nothing to commit/i.test(
|
|
5993
|
+
`${output.stdout}
|
|
5994
|
+
${output.stderr}`
|
|
5995
|
+
);
|
|
5996
|
+
return output.exitCode === 0 ? started(commit ? "Committed" : "Staged", state) : state.kind === "revert" && (state.phase === "conflicts" || empty) ? started("Stopped", state) : empty ? yield* operationFailure(
|
|
5997
|
+
"Empty",
|
|
5998
|
+
"Nothing to revert. The changes are already undone."
|
|
5999
|
+
) : (yield* requireGitSuccess(output), started("Stopped", state));
|
|
6000
|
+
});
|
|
6001
|
+
}
|
|
6002
|
+
function onBranch(git, directory) {
|
|
6003
|
+
return runRepositoryGitOutput(
|
|
6004
|
+
git,
|
|
6005
|
+
directory,
|
|
6006
|
+
["symbolic-ref", "--quiet", "HEAD"],
|
|
6007
|
+
{ exitCodes: [0, 1] }
|
|
6008
|
+
).pipe(Effect39.map((output) => output.exitCode === 0));
|
|
6009
|
+
}
|
|
6010
|
+
|
|
6011
|
+
// src/apps/server/features/repository-operations/repository-operations.ts
|
|
5414
6012
|
function repositoryOperationsFeature(dependencies) {
|
|
5415
6013
|
let { command, query } = repositoryRoutes(dependencies), { coordination } = dependencies, api = RepositoryOperationsApi;
|
|
5416
6014
|
return {
|
|
@@ -5424,42 +6022,45 @@ function repositoryOperationsFeature(dependencies) {
|
|
|
5424
6022
|
duringOperation: "proceed"
|
|
5425
6023
|
},
|
|
5426
6024
|
(input, git) => recoverRepositoryOperation(git, coordination, input)
|
|
6025
|
+
),
|
|
6026
|
+
command(
|
|
6027
|
+
api.start,
|
|
6028
|
+
(input) => ({
|
|
6029
|
+
name: input.operation._tag.toLowerCase(),
|
|
6030
|
+
locks: { refs: "wait", worktree: "wait" },
|
|
6031
|
+
duringOperation: "block"
|
|
6032
|
+
}),
|
|
6033
|
+
(input, git) => startOperation(git, coordination, input)
|
|
5427
6034
|
)
|
|
5428
6035
|
]
|
|
5429
6036
|
};
|
|
5430
6037
|
}
|
|
5431
6038
|
function recoverRepositoryOperation(git, coordination, command) {
|
|
5432
|
-
return
|
|
6039
|
+
return Effect40.gen(function* () {
|
|
5433
6040
|
let state = yield* coordination.operation(command.worktreePath);
|
|
5434
|
-
yield* validateRecoveryAction(state, command)
|
|
6041
|
+
if (yield* validateRecoveryAction(state, command), state.kind === "cherry-pick" && command.action === "continue" && (yield* continueStagedCherryPick(git, command.worktreePath)))
|
|
6042
|
+
return yield* coordination.operation(command.worktreePath).pipe(Effect40.mapError(uncertain));
|
|
5435
6043
|
let output = yield* git.run({
|
|
5436
6044
|
directory: command.worktreePath,
|
|
5437
|
-
arguments: [state.kind, `--${command.action}`],
|
|
6045
|
+
arguments: state.kind === "squash" ? ["reset", "--merge"] : [state.kind, `--${command.action}`],
|
|
5438
6046
|
timeoutMilliseconds: 12e4
|
|
5439
|
-
}).pipe(
|
|
5440
|
-
|
|
5441
|
-
() => operationError(
|
|
5442
|
-
"Uncertain",
|
|
5443
|
-
"Git's result could not be confirmed. Refresh the worktree before trying again."
|
|
5444
|
-
)
|
|
5445
|
-
)
|
|
5446
|
-
), operation = yield* coordination.operation(command.worktreePath).pipe(
|
|
5447
|
-
Effect36.mapError(
|
|
6047
|
+
}).pipe(Effect40.mapError(uncertain)), operation = yield* coordination.operation(command.worktreePath).pipe(
|
|
6048
|
+
Effect40.mapError(
|
|
5448
6049
|
() => operationError(
|
|
5449
6050
|
"Uncertain",
|
|
5450
6051
|
"Git finished, but its current state could not be read."
|
|
5451
6052
|
)
|
|
5452
6053
|
)
|
|
5453
6054
|
);
|
|
5454
|
-
return (command.action === "abort" || !
|
|
5455
|
-
}).pipe(
|
|
6055
|
+
return (command.action === "abort" || !advancedToStop(state, operation)) && (yield* requireGitSuccess(output)), operation;
|
|
6056
|
+
}).pipe(Effect40.uninterruptible);
|
|
5456
6057
|
}
|
|
5457
|
-
function
|
|
5458
|
-
return current.kind === previous.kind && current.phase === "conflicts" && (current.commit !== null && current.commit !== previous.commit || current.progress !== null && previous.progress !== null && current.progress.current > previous.progress.current);
|
|
6058
|
+
function advancedToStop(previous, current) {
|
|
6059
|
+
return current.kind === previous.kind && (current.phase === "conflicts" || current.phase === "empty") && (current.commit !== null && current.commit !== previous.commit || current.progress !== null && previous.progress !== null && current.progress.current > previous.progress.current);
|
|
5459
6060
|
}
|
|
5460
6061
|
function validateRecoveryAction(state, command) {
|
|
5461
6062
|
if (state.revision !== command.revision)
|
|
5462
|
-
return
|
|
6063
|
+
return Effect40.fail(
|
|
5463
6064
|
operationError(
|
|
5464
6065
|
"Stale",
|
|
5465
6066
|
"Git state changed. Review the refreshed operation before trying again."
|
|
@@ -5468,44 +6069,31 @@ function validateRecoveryAction(state, command) {
|
|
|
5468
6069
|
let action = state.actions.find(
|
|
5469
6070
|
(candidate) => candidate.action === command.action
|
|
5470
6071
|
);
|
|
5471
|
-
if (action?.enabled) return
|
|
6072
|
+
if (action?.enabled) return Effect40.void;
|
|
5472
6073
|
let detail = action?.reason ?? "This action is not supported by the current Git state.";
|
|
5473
|
-
return
|
|
6074
|
+
return Effect40.fail(
|
|
5474
6075
|
state.lock ? repositoryRejected("Busy", detail) : operationError("Incompatible", detail)
|
|
5475
6076
|
);
|
|
5476
6077
|
}
|
|
5477
|
-
function requireRecoverySuccess(output) {
|
|
5478
|
-
if (output.exitCode === 0) return Effect36.void;
|
|
5479
|
-
let detail = output.stderr || output.stdout || "Git rejected the action. Check the worktree and configured hooks.";
|
|
5480
|
-
return /\.lock['\s:]|another git process/i.test(detail) ? Effect36.fail(repositoryRejected("Busy", detail)) : Effect36.fail(
|
|
5481
|
-
operationError(
|
|
5482
|
-
/hook|pre-commit|commit-msg|pre-rebase/i.test(detail) ? "HookFailed" : "GitRejected",
|
|
5483
|
-
detail
|
|
5484
|
-
)
|
|
5485
|
-
);
|
|
5486
|
-
}
|
|
5487
|
-
function operationError(reason, detail) {
|
|
5488
|
-
return { _tag: "OperationFailed", reason, detail: detail.slice(0, 2048) };
|
|
5489
|
-
}
|
|
5490
6078
|
|
|
5491
6079
|
// src/apps/server/features/repository-pull/repository-pull.feature.ts
|
|
5492
|
-
import { Effect as
|
|
6080
|
+
import { Effect as Effect44 } from "effect";
|
|
5493
6081
|
|
|
5494
6082
|
// src/apps/server/features/repository-pull/fast-forward-branch.ts
|
|
5495
|
-
import { Effect as
|
|
6083
|
+
import { Effect as Effect41 } from "effect";
|
|
5496
6084
|
var pullCommand = { timeoutMilliseconds: 6e4 };
|
|
5497
6085
|
function fastForwardBranch(git, directory, branch, checkedOut) {
|
|
5498
|
-
return
|
|
6086
|
+
return Effect41.gen(function* () {
|
|
5499
6087
|
let tracked = yield* readTrackedBranch(git, directory, branch), upstreamTarget = yield* resolveUpstream(git, directory, tracked), { ahead, behind } = yield* countDivergence(
|
|
5500
6088
|
git,
|
|
5501
6089
|
directory,
|
|
5502
6090
|
tracked.target,
|
|
5503
6091
|
upstreamTarget
|
|
5504
6092
|
);
|
|
5505
|
-
return behind === 0 ? { outcome: "UpToDate" } : ahead > 0 ? yield*
|
|
6093
|
+
return behind === 0 ? { outcome: "UpToDate" } : ahead > 0 ? yield* Effect41.fail({
|
|
5506
6094
|
_tag: "PullDiverged",
|
|
5507
6095
|
upstream: tracked.upstream
|
|
5508
|
-
}) : (yield*
|
|
6096
|
+
}) : (yield* Effect41.uninterruptible(
|
|
5509
6097
|
checkedOut ? mergeFastForward(git, directory, tracked, upstreamTarget) : moveBranch(git, directory, tracked, upstreamTarget)
|
|
5510
6098
|
), { outcome: "FastForwarded" });
|
|
5511
6099
|
});
|
|
@@ -5522,10 +6110,10 @@ function readTrackedBranch(git, directory, branch) {
|
|
|
5522
6110
|
],
|
|
5523
6111
|
pullCommand
|
|
5524
6112
|
).pipe(
|
|
5525
|
-
|
|
6113
|
+
Effect41.flatMap((output) => {
|
|
5526
6114
|
let [, target, upstreamRef, upstream] = output.split(`
|
|
5527
6115
|
`).map((line) => line.split("\0")).find(([name]) => name === ref) ?? [];
|
|
5528
|
-
return target === void 0 ?
|
|
6116
|
+
return target === void 0 ? Effect41.fail({ _tag: "BranchMissing" }) : !upstreamRef || !upstream ? Effect41.fail({ _tag: "UpstreamMissing" }) : Effect41.succeed({
|
|
5529
6117
|
name: branch,
|
|
5530
6118
|
target,
|
|
5531
6119
|
upstreamRef,
|
|
@@ -5541,11 +6129,11 @@ function resolveUpstream(git, directory, tracked) {
|
|
|
5541
6129
|
["rev-parse", "--verify", "--quiet", `${tracked.upstreamRef}^{commit}`],
|
|
5542
6130
|
{ ...pullCommand, exitCodes: [0, 1] }
|
|
5543
6131
|
).pipe(
|
|
5544
|
-
|
|
5545
|
-
(output) => output.trim() === "" ?
|
|
6132
|
+
Effect41.flatMap(
|
|
6133
|
+
(output) => output.trim() === "" ? Effect41.fail({
|
|
5546
6134
|
_tag: "UpstreamMissing",
|
|
5547
6135
|
upstream: tracked.upstream
|
|
5548
|
-
}) :
|
|
6136
|
+
}) : Effect41.succeed(output.trim())
|
|
5549
6137
|
)
|
|
5550
6138
|
);
|
|
5551
6139
|
}
|
|
@@ -5556,7 +6144,7 @@ function countDivergence(git, directory, target, upstreamTarget) {
|
|
|
5556
6144
|
["rev-list", "--left-right", "--count", `${target}...${upstreamTarget}`],
|
|
5557
6145
|
pullCommand
|
|
5558
6146
|
).pipe(
|
|
5559
|
-
|
|
6147
|
+
Effect41.map((output) => {
|
|
5560
6148
|
let [ahead = 0, behind = 0] = output.trim().split(/\s+/).map(Number);
|
|
5561
6149
|
return { ahead, behind };
|
|
5562
6150
|
})
|
|
@@ -5576,8 +6164,8 @@ function mergeFastForward(git, directory, tracked, upstreamTarget) {
|
|
|
5576
6164
|
],
|
|
5577
6165
|
pullCommand
|
|
5578
6166
|
).pipe(
|
|
5579
|
-
|
|
5580
|
-
|
|
6167
|
+
Effect41.asVoid,
|
|
6168
|
+
Effect41.mapError((error) => mergeFailure(error, tracked))
|
|
5581
6169
|
);
|
|
5582
6170
|
}
|
|
5583
6171
|
function mergeFailure(error, tracked) {
|
|
@@ -5604,8 +6192,8 @@ function moveBranch(git, directory, tracked, upstreamTarget) {
|
|
|
5604
6192
|
],
|
|
5605
6193
|
pullCommand
|
|
5606
6194
|
).pipe(
|
|
5607
|
-
|
|
5608
|
-
|
|
6195
|
+
Effect41.asVoid,
|
|
6196
|
+
Effect41.mapError(
|
|
5609
6197
|
(error) => isGitRejection(error) ? branchMoved(tracked) : { _tag: "PullUncertain" }
|
|
5610
6198
|
)
|
|
5611
6199
|
);
|
|
@@ -5618,7 +6206,7 @@ function pullBlocked(detail) {
|
|
|
5618
6206
|
}
|
|
5619
6207
|
|
|
5620
6208
|
// src/apps/server/features/repository-pull/repository-fetch.ts
|
|
5621
|
-
import { Cause, Effect as
|
|
6209
|
+
import { Cause, Effect as Effect42, Fiber as Fiber2, Option, Semaphore } from "effect";
|
|
5622
6210
|
var defaultIntervalSeconds = 300, maximumFetchedRepositories = 32, settingKey = "rebase.autoFetchIntervalSeconds";
|
|
5623
6211
|
function acquireRepositoryFetch({
|
|
5624
6212
|
access,
|
|
@@ -5626,24 +6214,24 @@ function acquireRepositoryFetch({
|
|
|
5626
6214
|
events,
|
|
5627
6215
|
git
|
|
5628
6216
|
}) {
|
|
5629
|
-
return
|
|
5630
|
-
let scope = yield*
|
|
6217
|
+
return Effect42.gen(function* () {
|
|
6218
|
+
let scope = yield* Effect42.scope, mutex = yield* Semaphore.make(1), repositories = /* @__PURE__ */ new Map(), status = (repository) => ({
|
|
5631
6219
|
fetching: repository.fetching !== void 0,
|
|
5632
6220
|
defaultIntervalSeconds,
|
|
5633
6221
|
setting: repository.setting,
|
|
5634
6222
|
...repository.failure === void 0 ? {} : { failure: repository.failure }
|
|
5635
|
-
}), publish = (repository) => events.publishChanged([...repository.repositoryIds], "Fetch"), schedule = (repository) =>
|
|
6223
|
+
}), publish = (repository) => events.publishChanged([...repository.repositoryIds], "Fetch"), schedule = (repository) => Effect42.gen(function* () {
|
|
5636
6224
|
let previous = repository.schedule;
|
|
5637
6225
|
if (repository.schedule = void 0, previous !== void 0 && (yield* Fiber2.interrupt(previous)), repository.fetching !== void 0 || repository.setting._tag === "Disabled")
|
|
5638
6226
|
return;
|
|
5639
6227
|
let seconds = repository.setting._tag === "Interval" ? repository.setting.seconds : defaultIntervalSeconds;
|
|
5640
|
-
repository.schedule = yield*
|
|
5641
|
-
|
|
5642
|
-
|
|
6228
|
+
repository.schedule = yield* Effect42.sleep(seconds * 1e3).pipe(
|
|
6229
|
+
Effect42.andThen(tick(repository)),
|
|
6230
|
+
Effect42.forkIn(scope)
|
|
5643
6231
|
);
|
|
5644
|
-
}), tick = (repository) =>
|
|
6232
|
+
}), tick = (repository) => Effect42.gen(function* () {
|
|
5645
6233
|
repository.schedule = void 0, events.listening() ? yield* begin(repository) : yield* schedule(repository);
|
|
5646
|
-
}).pipe(Semaphore.withPermit(mutex)), begin = (repository) =>
|
|
6234
|
+
}).pipe(Semaphore.withPermit(mutex)), begin = (repository) => Effect42.gen(function* () {
|
|
5647
6235
|
if (repository.fetching !== void 0) return repository.fetching;
|
|
5648
6236
|
let previous = repository.schedule;
|
|
5649
6237
|
repository.schedule = void 0, previous !== void 0 && (yield* Fiber2.interrupt(previous));
|
|
@@ -5652,15 +6240,15 @@ function acquireRepositoryFetch({
|
|
|
5652
6240
|
coordination,
|
|
5653
6241
|
repository.path
|
|
5654
6242
|
).pipe(
|
|
5655
|
-
|
|
5656
|
-
(failure) =>
|
|
6243
|
+
Effect42.tap(
|
|
6244
|
+
(failure) => Effect42.gen(function* () {
|
|
5657
6245
|
repository.fetching = void 0, failure === void 0 ? delete repository.failure : repository.failure = failure, publish(repository), yield* schedule(repository);
|
|
5658
6246
|
}).pipe(Semaphore.withPermit(mutex))
|
|
5659
6247
|
),
|
|
5660
|
-
|
|
6248
|
+
Effect42.forkIn(scope)
|
|
5661
6249
|
);
|
|
5662
6250
|
return repository.fetching = fetching, publish(repository), fetching;
|
|
5663
|
-
}), register = (repositoryId) =>
|
|
6251
|
+
}), register = (repositoryId) => Effect42.gen(function* () {
|
|
5664
6252
|
let entry = yield* access.repository(repositoryId), key = entry.logicalRepositoryId ?? entry.id, known = repositories.get(key);
|
|
5665
6253
|
if (known !== void 0)
|
|
5666
6254
|
return known.repositoryIds.add(repositoryId), repositories.delete(key), repositories.set(key, known), known;
|
|
@@ -5670,28 +6258,28 @@ function acquireRepositoryFetch({
|
|
|
5670
6258
|
setting: yield* readFetchSetting(git, entry.path)
|
|
5671
6259
|
};
|
|
5672
6260
|
return repositories.set(key, repository), yield* evictOldest, repository.setting._tag !== "Disabled" && (yield* begin(repository)), repository;
|
|
5673
|
-
}).pipe(Semaphore.withPermit(mutex)), evictOldest =
|
|
6261
|
+
}).pipe(Semaphore.withPermit(mutex)), evictOldest = Effect42.suspend(() => {
|
|
5674
6262
|
let oldest = repositories.entries().next().value;
|
|
5675
6263
|
if (repositories.size <= maximumFetchedRepositories || oldest === void 0)
|
|
5676
|
-
return
|
|
6264
|
+
return Effect42.void;
|
|
5677
6265
|
repositories.delete(oldest[0]);
|
|
5678
6266
|
let { schedule: scheduled, fetching } = oldest[1];
|
|
5679
|
-
return
|
|
5680
|
-
scheduled === void 0 ?
|
|
5681
|
-
fetching === void 0 ?
|
|
6267
|
+
return Effect42.all([
|
|
6268
|
+
scheduled === void 0 ? Effect42.void : Fiber2.interrupt(scheduled),
|
|
6269
|
+
fetching === void 0 ? Effect42.void : Fiber2.interrupt(fetching)
|
|
5682
6270
|
]);
|
|
5683
6271
|
});
|
|
5684
6272
|
return {
|
|
5685
|
-
status: (repositoryId) => register(repositoryId).pipe(
|
|
5686
|
-
fetch: (repositoryId) =>
|
|
6273
|
+
status: (repositoryId) => register(repositoryId).pipe(Effect42.map(status)),
|
|
6274
|
+
fetch: (repositoryId) => Effect42.gen(function* () {
|
|
5687
6275
|
let repository = yield* register(repositoryId), fetching = yield* begin(repository).pipe(
|
|
5688
6276
|
Semaphore.withPermit(mutex)
|
|
5689
6277
|
), failure = yield* Fiber2.join(fetching);
|
|
5690
|
-
return failure !== void 0 ? yield*
|
|
6278
|
+
return failure !== void 0 ? yield* Effect42.fail(failure) : status(repository);
|
|
5691
6279
|
}),
|
|
5692
|
-
configure: (repositoryId, setting) =>
|
|
6280
|
+
configure: (repositoryId, setting) => Effect42.gen(function* () {
|
|
5693
6281
|
let repository = yield* register(repositoryId);
|
|
5694
|
-
return yield*
|
|
6282
|
+
return yield* Effect42.gen(function* () {
|
|
5695
6283
|
return yield* writeFetchSetting(git, repository.path, setting), repository.setting = setting, yield* schedule(repository), publish(repository), status(repository);
|
|
5696
6284
|
}).pipe(Semaphore.withPermit(mutex));
|
|
5697
6285
|
})
|
|
@@ -5708,12 +6296,12 @@ function runFetch(git, coordination, path) {
|
|
|
5708
6296
|
},
|
|
5709
6297
|
runRepositoryGit(git, path, ["fetch"], { timeoutMilliseconds: 12e4 })
|
|
5710
6298
|
).pipe(
|
|
5711
|
-
|
|
5712
|
-
|
|
6299
|
+
Effect42.as(void 0),
|
|
6300
|
+
Effect42.catchCause((cause) => {
|
|
5713
6301
|
if (Cause.hasInterrupts(cause))
|
|
5714
|
-
return
|
|
6302
|
+
return Effect42.failCause(Cause.interrupt());
|
|
5715
6303
|
let error = Cause.findErrorOption(cause);
|
|
5716
|
-
return
|
|
6304
|
+
return Effect42.succeed({
|
|
5717
6305
|
_tag: "FetchFailed",
|
|
5718
6306
|
reason: Option.isSome(error) && error.value._tag === "GitFailed" ? error.value.reason : "Failed"
|
|
5719
6307
|
});
|
|
@@ -5727,11 +6315,11 @@ function readFetchSetting(git, path) {
|
|
|
5727
6315
|
["config", "--local", "--get", settingKey],
|
|
5728
6316
|
{ exitCodes: [0, 1] }
|
|
5729
6317
|
).pipe(
|
|
5730
|
-
|
|
6318
|
+
Effect42.map((output) => {
|
|
5731
6319
|
let value = output.trim(), seconds = Number(value);
|
|
5732
6320
|
return value === "0" ? { _tag: "Disabled" } : Number.isInteger(seconds) && seconds > 0 && seconds <= 86400 ? { _tag: "Interval", seconds } : { _tag: "Inherit" };
|
|
5733
6321
|
}),
|
|
5734
|
-
|
|
6322
|
+
Effect42.mapError(settingsUnavailable)
|
|
5735
6323
|
);
|
|
5736
6324
|
}
|
|
5737
6325
|
function writeFetchSetting(git, path, setting) {
|
|
@@ -5741,7 +6329,7 @@ function writeFetchSetting(git, path, setting) {
|
|
|
5741
6329
|
"--local",
|
|
5742
6330
|
settingKey,
|
|
5743
6331
|
value
|
|
5744
|
-
]).pipe(
|
|
6332
|
+
]).pipe(Effect42.asVoid, Effect42.mapError(settingsUnavailable));
|
|
5745
6333
|
}
|
|
5746
6334
|
function settingsUnavailable() {
|
|
5747
6335
|
return repositoryRejected(
|
|
@@ -5753,29 +6341,29 @@ function settingsUnavailable() {
|
|
|
5753
6341
|
// src/apps/server/repository/repository-access.ts
|
|
5754
6342
|
import { realpath as realpath4 } from "node:fs";
|
|
5755
6343
|
import { promisify as promisify3 } from "node:util";
|
|
5756
|
-
import { Effect as
|
|
6344
|
+
import { Effect as Effect43 } from "effect";
|
|
5757
6345
|
var realpathNative3 = promisify3(realpath4.native), branchPrefix = "refs/heads/";
|
|
5758
6346
|
function createRepositoryAccess(catalog, git, watcher) {
|
|
5759
6347
|
let worktreePaths = createWorktreePathCache(git, watcher), repository = (repositoryId) => catalog.find(repositoryId).pipe(
|
|
5760
|
-
|
|
5761
|
-
(entry) => entry === void 0 ?
|
|
6348
|
+
Effect43.flatMap(
|
|
6349
|
+
(entry) => entry === void 0 ? Effect43.fail(
|
|
5762
6350
|
repositoryRejected(
|
|
5763
6351
|
"Missing",
|
|
5764
6352
|
"This repository is no longer available."
|
|
5765
6353
|
)
|
|
5766
|
-
) :
|
|
6354
|
+
) : Effect43.succeed(entry)
|
|
5767
6355
|
)
|
|
5768
6356
|
);
|
|
5769
6357
|
return {
|
|
5770
6358
|
repository,
|
|
5771
6359
|
worktrees: (repositoryPath) => readWorktrees(git, repositoryPath).pipe(
|
|
5772
|
-
|
|
5773
|
-
|
|
6360
|
+
Effect43.flatMap(canonicalizeWorktrees),
|
|
6361
|
+
Effect43.mapError(worktreesUnreadable)
|
|
5774
6362
|
),
|
|
5775
|
-
requireWorktree: (scope) =>
|
|
6363
|
+
requireWorktree: (scope) => Effect43.gen(function* () {
|
|
5776
6364
|
let entry = yield* repository(scope.repositoryId);
|
|
5777
|
-
if (!(yield* worktreePaths.contains(entry.path, scope.worktreePath).pipe(
|
|
5778
|
-
return yield*
|
|
6365
|
+
if (!(yield* worktreePaths.contains(entry.path, scope.worktreePath).pipe(Effect43.mapError(worktreesUnreadable))))
|
|
6366
|
+
return yield* Effect43.fail(
|
|
5779
6367
|
repositoryRejected(
|
|
5780
6368
|
"Missing",
|
|
5781
6369
|
"This worktree does not belong to the repository."
|
|
@@ -5790,12 +6378,12 @@ function readWorktrees(git, directory) {
|
|
|
5790
6378
|
directory,
|
|
5791
6379
|
["worktree", "list", "--porcelain", "-z"],
|
|
5792
6380
|
{ timeoutMilliseconds: 15e3 }
|
|
5793
|
-
).pipe(
|
|
6381
|
+
).pipe(Effect43.map(parseWorktreeList));
|
|
5794
6382
|
}
|
|
5795
6383
|
function canonicalizeWorktrees(worktrees) {
|
|
5796
|
-
return
|
|
6384
|
+
return Effect43.all(
|
|
5797
6385
|
worktrees.map(
|
|
5798
|
-
(worktree) =>
|
|
6386
|
+
(worktree) => Effect43.promise(async () => ({
|
|
5799
6387
|
...worktree,
|
|
5800
6388
|
path: await realpathNative3(worktree.path).catch(() => worktree.path)
|
|
5801
6389
|
}))
|
|
@@ -5816,13 +6404,13 @@ function createWorktreePathCache(git, watcher) {
|
|
|
5816
6404
|
entry.watch = void 0, watch3?.close();
|
|
5817
6405
|
}, reload = (repositoryPath) => {
|
|
5818
6406
|
let entry = {}, changed = !1;
|
|
5819
|
-
return
|
|
6407
|
+
return Effect43.gen(function* () {
|
|
5820
6408
|
let directory = yield* readGitCommonDirectory(git, repositoryPath);
|
|
5821
6409
|
entry.watch = yield* watcher.watch(directory, (kind) => {
|
|
5822
6410
|
kind !== "Index" && (changed = !0, forget(repositoryPath, entry));
|
|
5823
6411
|
});
|
|
5824
6412
|
let worktrees = yield* readWorktrees(git, repositoryPath).pipe(
|
|
5825
|
-
|
|
6413
|
+
Effect43.flatMap(canonicalizeWorktrees)
|
|
5826
6414
|
);
|
|
5827
6415
|
if (entry.paths = new Set(worktrees.map((worktree) => worktree.path)), !changed) {
|
|
5828
6416
|
let previous = cache.get(repositoryPath);
|
|
@@ -5830,16 +6418,16 @@ function createWorktreePathCache(git, watcher) {
|
|
|
5830
6418
|
}
|
|
5831
6419
|
return entry.paths;
|
|
5832
6420
|
}).pipe(
|
|
5833
|
-
|
|
5834
|
-
() =>
|
|
6421
|
+
Effect43.onExit(
|
|
6422
|
+
() => Effect43.sync(() => {
|
|
5835
6423
|
cache.get(repositoryPath) !== entry && forget(repositoryPath, entry);
|
|
5836
6424
|
})
|
|
5837
6425
|
)
|
|
5838
6426
|
);
|
|
5839
6427
|
};
|
|
5840
6428
|
return {
|
|
5841
|
-
contains: (repositoryPath, worktreePath) => cache.get(repositoryPath)?.paths?.has(worktreePath) ?
|
|
5842
|
-
|
|
6429
|
+
contains: (repositoryPath, worktreePath) => cache.get(repositoryPath)?.paths?.has(worktreePath) ? Effect43.succeed(!0) : reload(repositoryPath).pipe(
|
|
6430
|
+
Effect43.map((paths) => paths.has(worktreePath))
|
|
5843
6431
|
)
|
|
5844
6432
|
};
|
|
5845
6433
|
}
|
|
@@ -5882,7 +6470,7 @@ function worktreeFromEntry(entry, main2) {
|
|
|
5882
6470
|
|
|
5883
6471
|
// src/apps/server/features/repository-pull/repository-pull.feature.ts
|
|
5884
6472
|
function repositoryPullFeature(dependencies) {
|
|
5885
|
-
return
|
|
6473
|
+
return Effect44.gen(function* () {
|
|
5886
6474
|
let { command } = repositoryRoutes(dependencies), fetch = yield* acquireRepositoryFetch(dependencies);
|
|
5887
6475
|
return {
|
|
5888
6476
|
routes: [
|
|
@@ -5912,7 +6500,7 @@ function repositoryPullFeature(dependencies) {
|
|
|
5912
6500
|
});
|
|
5913
6501
|
}
|
|
5914
6502
|
function pullBranch(coordination) {
|
|
5915
|
-
return (command, git) =>
|
|
6503
|
+
return (command, git) => Effect44.gen(function* () {
|
|
5916
6504
|
let checkout = yield* findCheckout(git, command), directory = checkout ?? command.worktreePath;
|
|
5917
6505
|
return yield* coordination.run(
|
|
5918
6506
|
directory,
|
|
@@ -5922,7 +6510,7 @@ function pullBranch(coordination) {
|
|
|
5922
6510
|
duringOperation: "block"
|
|
5923
6511
|
},
|
|
5924
6512
|
requireSameCheckout(git, command, checkout).pipe(
|
|
5925
|
-
|
|
6513
|
+
Effect44.andThen(
|
|
5926
6514
|
fastForwardBranch(
|
|
5927
6515
|
git,
|
|
5928
6516
|
directory,
|
|
@@ -5936,7 +6524,7 @@ function pullBranch(coordination) {
|
|
|
5936
6524
|
}
|
|
5937
6525
|
function requireSameCheckout(git, command, checkout) {
|
|
5938
6526
|
return findCheckout(git, command).pipe(
|
|
5939
|
-
|
|
6527
|
+
Effect44.filterOrFail(
|
|
5940
6528
|
(current) => current === checkout,
|
|
5941
6529
|
() => pullBlocked(`${command.branch} was checked out while pulling`)
|
|
5942
6530
|
)
|
|
@@ -5944,15 +6532,15 @@ function requireSameCheckout(git, command, checkout) {
|
|
|
5944
6532
|
}
|
|
5945
6533
|
function findCheckout(git, command) {
|
|
5946
6534
|
return readWorktrees(git, command.worktreePath).pipe(
|
|
5947
|
-
|
|
5948
|
-
|
|
6535
|
+
Effect44.flatMap(canonicalizeWorktrees),
|
|
6536
|
+
Effect44.map(
|
|
5949
6537
|
(worktrees) => worktrees.find((worktree) => worktree.head.branch === command.branch)?.path
|
|
5950
6538
|
)
|
|
5951
6539
|
);
|
|
5952
6540
|
}
|
|
5953
6541
|
|
|
5954
6542
|
// src/apps/server/features/repository-push/repository-push.ts
|
|
5955
|
-
import { Effect as
|
|
6543
|
+
import { Effect as Effect46 } from "effect";
|
|
5956
6544
|
|
|
5957
6545
|
// src/apps/server/features/repository-push/git/push-failures.ts
|
|
5958
6546
|
function pushError(reason, detail) {
|
|
@@ -5993,11 +6581,11 @@ function classifyPushFailure(output, destinationRef) {
|
|
|
5993
6581
|
}
|
|
5994
6582
|
|
|
5995
6583
|
// src/apps/server/features/repository-push/git/reconcile-remote-branch.ts
|
|
5996
|
-
import { Effect as
|
|
6584
|
+
import { Effect as Effect45 } from "effect";
|
|
5997
6585
|
var reconcileTimeoutMilliseconds = 3e4;
|
|
5998
6586
|
function reconcileRemoteBranch(git, directory, { remote, branch }) {
|
|
5999
6587
|
let remoteRef = `refs/heads/${branch}`, trackingRef = `refs/remotes/${remote}/${branch}`, options = { timeoutMilliseconds: reconcileTimeoutMilliseconds };
|
|
6000
|
-
return
|
|
6588
|
+
return Effect45.gen(function* () {
|
|
6001
6589
|
let target = (yield* runRepositoryGit(
|
|
6002
6590
|
git,
|
|
6003
6591
|
directory,
|
|
@@ -6026,13 +6614,13 @@ function reconcileRemoteBranch(git, directory, { remote, branch }) {
|
|
|
6026
6614
|
function uncertainPush(git, directory, destination) {
|
|
6027
6615
|
let name = `${destination.remote}/${destination.branch}`;
|
|
6028
6616
|
return reconcileRemoteBranch(git, directory, destination).pipe(
|
|
6029
|
-
|
|
6617
|
+
Effect45.map(
|
|
6030
6618
|
(target) => target === null ? `Push unconfirmed. ${name} no longer exists.` : `Push unconfirmed. ${name} is at ${target.slice(0, 8)}.`
|
|
6031
6619
|
),
|
|
6032
|
-
|
|
6620
|
+
Effect45.orElseSucceed(
|
|
6033
6621
|
() => `Push unconfirmed and ${name} could not be read. Fetch to check.`
|
|
6034
6622
|
),
|
|
6035
|
-
|
|
6623
|
+
Effect45.flatMap((detail) => Effect45.fail(pushError("Uncertain", detail)))
|
|
6036
6624
|
);
|
|
6037
6625
|
}
|
|
6038
6626
|
|
|
@@ -6040,7 +6628,7 @@ function uncertainPush(git, directory, destination) {
|
|
|
6040
6628
|
var pushTimeoutMilliseconds = 12e4;
|
|
6041
6629
|
function pushRemoteBranch(git, command) {
|
|
6042
6630
|
let directory = command.worktreePath, destinationRef = `refs/heads/${command.destination.branch}`;
|
|
6043
|
-
return
|
|
6631
|
+
return Effect46.gen(function* () {
|
|
6044
6632
|
yield* requireDestination(git, directory, command.destination);
|
|
6045
6633
|
let target = yield* readBranchTip(git, directory, command.branch);
|
|
6046
6634
|
return yield* runPush(
|
|
@@ -6059,18 +6647,66 @@ function pushRemoteBranch(git, command) {
|
|
|
6059
6647
|
};
|
|
6060
6648
|
});
|
|
6061
6649
|
}
|
|
6650
|
+
function pushTagsToRemote(git, { worktreePath, remote, tags }) {
|
|
6651
|
+
let refs = tags.map((tag) => `refs/tags/${tag}`);
|
|
6652
|
+
return Effect46.gen(function* () {
|
|
6653
|
+
yield* requireRemote(git, worktreePath, remote);
|
|
6654
|
+
let output = yield* git.run({
|
|
6655
|
+
directory: worktreePath,
|
|
6656
|
+
arguments: [
|
|
6657
|
+
"push",
|
|
6658
|
+
"--porcelain",
|
|
6659
|
+
"--atomic",
|
|
6660
|
+
remote,
|
|
6661
|
+
...refs.map((ref) => `${ref}:${ref}`)
|
|
6662
|
+
],
|
|
6663
|
+
timeoutMilliseconds: pushTimeoutMilliseconds
|
|
6664
|
+
}).pipe(
|
|
6665
|
+
Effect46.mapError(
|
|
6666
|
+
(error) => error.reason === "Timeout" ? pushError(
|
|
6667
|
+
"Uncertain",
|
|
6668
|
+
`The push to ${remote} did not finish. Fetch to see which tags arrived.`
|
|
6669
|
+
) : repositoryRejected(
|
|
6670
|
+
"GitFailed",
|
|
6671
|
+
`Git could not run the push (${error.reason}).`
|
|
6672
|
+
)
|
|
6673
|
+
)
|
|
6674
|
+
), statuses = tags.map((tag, index2) => ({
|
|
6675
|
+
tag,
|
|
6676
|
+
status: pushRefStatus(output.stdout, refs[index2] ?? "")
|
|
6677
|
+
}));
|
|
6678
|
+
if (output.exitCode === 0)
|
|
6679
|
+
return {
|
|
6680
|
+
remote,
|
|
6681
|
+
pushed: statuses.filter(({ status }) => status?.flag !== "=").map(({ tag }) => tag),
|
|
6682
|
+
upToDate: statuses.filter(({ status }) => status?.flag === "=").map(({ tag }) => tag)
|
|
6683
|
+
};
|
|
6684
|
+
let existing = statuses.filter(({ status }) => status?.summary.includes("(already exists)")).map(({ tag }) => tag);
|
|
6685
|
+
return yield* Effect46.fail(
|
|
6686
|
+
existing.length > 0 ? pushError("TagExists", existing.join(", ")) : classifyPushFailure(output, refs[0] ?? "")
|
|
6687
|
+
);
|
|
6688
|
+
});
|
|
6689
|
+
}
|
|
6690
|
+
function requireRemote(git, directory, remote) {
|
|
6691
|
+
return runRepositoryGit(git, directory, ["remote"]).pipe(
|
|
6692
|
+
Effect46.flatMap(
|
|
6693
|
+
(remotes) => remotes.split(`
|
|
6694
|
+
`).includes(remote) ? Effect46.void : Effect46.fail(
|
|
6695
|
+
pushError(
|
|
6696
|
+
"RemoteMissing",
|
|
6697
|
+
`The remote "${remote}" does not exist.`
|
|
6698
|
+
)
|
|
6699
|
+
)
|
|
6700
|
+
)
|
|
6701
|
+
);
|
|
6702
|
+
}
|
|
6062
6703
|
function requireDestination(git, directory, { remote, branch }) {
|
|
6063
|
-
return
|
|
6064
|
-
|
|
6065
|
-
`).includes(remote))
|
|
6066
|
-
return yield* Effect42.fail(
|
|
6067
|
-
pushError("RemoteMissing", `The remote "${remote}" does not exist.`)
|
|
6068
|
-
);
|
|
6069
|
-
yield* runRepositoryGit(git, directory, [
|
|
6704
|
+
return Effect46.gen(function* () {
|
|
6705
|
+
yield* requireRemote(git, directory, remote), yield* runRepositoryGit(git, directory, [
|
|
6070
6706
|
"check-ref-format",
|
|
6071
6707
|
`refs/heads/${branch}`
|
|
6072
6708
|
]).pipe(
|
|
6073
|
-
|
|
6709
|
+
Effect46.mapError(
|
|
6074
6710
|
() => pushError("InvalidBranch", `"${branch}" is not a valid branch name.`)
|
|
6075
6711
|
)
|
|
6076
6712
|
);
|
|
@@ -6083,8 +6719,8 @@ function readBranchTip(git, directory, branch) {
|
|
|
6083
6719
|
"--quiet",
|
|
6084
6720
|
`refs/heads/${branch}^{commit}`
|
|
6085
6721
|
]).pipe(
|
|
6086
|
-
|
|
6087
|
-
|
|
6722
|
+
Effect46.map((output) => output.trim()),
|
|
6723
|
+
Effect46.mapError(
|
|
6088
6724
|
() => pushError("InvalidBranch", `The branch "${branch}" does not exist.`)
|
|
6089
6725
|
)
|
|
6090
6726
|
);
|
|
@@ -6096,23 +6732,23 @@ function runPush(git, directory, args, destination) {
|
|
|
6096
6732
|
arguments: ["push", "--porcelain", ...args],
|
|
6097
6733
|
timeoutMilliseconds: pushTimeoutMilliseconds
|
|
6098
6734
|
}).pipe(
|
|
6099
|
-
|
|
6100
|
-
(error) => error.reason === "Timeout" ? uncertainPush(git, directory, destination) :
|
|
6735
|
+
Effect46.catch(
|
|
6736
|
+
(error) => error.reason === "Timeout" ? uncertainPush(git, directory, destination) : Effect46.fail(
|
|
6101
6737
|
repositoryRejected(
|
|
6102
6738
|
"GitFailed",
|
|
6103
6739
|
`Git could not run the push (${error.reason}).`
|
|
6104
6740
|
)
|
|
6105
6741
|
)
|
|
6106
6742
|
),
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
() => reconcileRemoteBranch(git, directory, destination).pipe(
|
|
6743
|
+
Effect46.flatMap((output) => requirePushed(output, destinationRef)),
|
|
6744
|
+
Effect46.onInterrupt(
|
|
6745
|
+
() => reconcileRemoteBranch(git, directory, destination).pipe(Effect46.ignore)
|
|
6110
6746
|
)
|
|
6111
6747
|
);
|
|
6112
6748
|
}
|
|
6113
6749
|
function requirePushed(output, destinationRef) {
|
|
6114
6750
|
let status = pushRefStatus(output.stdout, destinationRef);
|
|
6115
|
-
return output.exitCode === 0 && status !== void 0 && status.flag !== "!" ?
|
|
6751
|
+
return output.exitCode === 0 && status !== void 0 && status.flag !== "!" ? Effect46.void : Effect46.fail(classifyPushFailure(output, destinationRef));
|
|
6116
6752
|
}
|
|
6117
6753
|
function repositoryPushFeature(dependencies) {
|
|
6118
6754
|
let { command } = repositoryRoutes(dependencies);
|
|
@@ -6122,6 +6758,11 @@ function repositoryPushFeature(dependencies) {
|
|
|
6122
6758
|
RepositoryPushApi.push,
|
|
6123
6759
|
{ name: "push", locks: { refs: "wait" }, duringOperation: "block" },
|
|
6124
6760
|
(input, git) => pushRemoteBranch(git, input)
|
|
6761
|
+
),
|
|
6762
|
+
command(
|
|
6763
|
+
RepositoryPushApi.pushTags,
|
|
6764
|
+
{ name: "push", locks: { refs: "wait" }, duringOperation: "proceed" },
|
|
6765
|
+
(input, git) => pushTagsToRemote(git, input)
|
|
6125
6766
|
)
|
|
6126
6767
|
]
|
|
6127
6768
|
};
|
|
@@ -6129,7 +6770,7 @@ function repositoryPushFeature(dependencies) {
|
|
|
6129
6770
|
|
|
6130
6771
|
// src/apps/server/features/repository-reflog/repository-reflog.ts
|
|
6131
6772
|
import { createHash as createHash3 } from "node:crypto";
|
|
6132
|
-
import { Effect as
|
|
6773
|
+
import { Effect as Effect48 } from "effect";
|
|
6133
6774
|
|
|
6134
6775
|
// src/apps/server/features/repository-reflog/reflog-entries.ts
|
|
6135
6776
|
function parseReflog(output) {
|
|
@@ -6248,7 +6889,7 @@ function text2(value) {
|
|
|
6248
6889
|
}
|
|
6249
6890
|
|
|
6250
6891
|
// src/apps/server/features/repository-refs/git/ref-git.ts
|
|
6251
|
-
import { Effect as
|
|
6892
|
+
import { Effect as Effect47 } from "effect";
|
|
6252
6893
|
var refCommand = {
|
|
6253
6894
|
literalPathspecs: !1,
|
|
6254
6895
|
timeoutMilliseconds: 3e4
|
|
@@ -6260,8 +6901,8 @@ function requireValidRefName(git, directory, fullRef, invalid) {
|
|
|
6260
6901
|
["check-ref-format", fullRef],
|
|
6261
6902
|
refCommand
|
|
6262
6903
|
).pipe(
|
|
6263
|
-
|
|
6264
|
-
|
|
6904
|
+
Effect47.catchIf(isGitRejection, () => Effect47.fail(invalid)),
|
|
6905
|
+
Effect47.asVoid
|
|
6265
6906
|
);
|
|
6266
6907
|
}
|
|
6267
6908
|
function readRefTarget(git, directory, revision) {
|
|
@@ -6270,7 +6911,7 @@ function readRefTarget(git, directory, revision) {
|
|
|
6270
6911
|
directory,
|
|
6271
6912
|
["rev-parse", "--verify", "--quiet", revision],
|
|
6272
6913
|
{ ...refCommand, exitCodes: [0, 1] }
|
|
6273
|
-
).pipe(
|
|
6914
|
+
).pipe(Effect47.map((output) => output.trim() || void 0));
|
|
6274
6915
|
}
|
|
6275
6916
|
|
|
6276
6917
|
// src/apps/server/features/repository-reflog/repository-reflog.ts
|
|
@@ -6293,8 +6934,8 @@ function repositoryReflogFeature(dependencies) {
|
|
|
6293
6934
|
};
|
|
6294
6935
|
}
|
|
6295
6936
|
function readReflog(git, { worktreePath, ref }) {
|
|
6296
|
-
return
|
|
6297
|
-
let [output, tips] = yield*
|
|
6937
|
+
return Effect48.gen(function* () {
|
|
6938
|
+
let [output, tips] = yield* Effect48.all(
|
|
6298
6939
|
[
|
|
6299
6940
|
readReflogOutput(git, worktreePath, ref),
|
|
6300
6941
|
runRepositoryGit(
|
|
@@ -6339,20 +6980,20 @@ function readReflogOutput(git, directory, ref) {
|
|
|
6339
6980
|
],
|
|
6340
6981
|
{ maxOutputBytes: 8 * 1048576 }
|
|
6341
6982
|
).pipe(
|
|
6342
|
-
|
|
6983
|
+
Effect48.catchIf(
|
|
6343
6984
|
(error) => /does not have any commits yet/i.test(error.detail),
|
|
6344
|
-
() =>
|
|
6985
|
+
() => Effect48.succeed("")
|
|
6345
6986
|
)
|
|
6346
6987
|
);
|
|
6347
6988
|
}
|
|
6348
6989
|
function readOrphaned(git, directory, oids, tips) {
|
|
6349
|
-
return oids.length === 0 ?
|
|
6990
|
+
return oids.length === 0 ? Effect48.succeed(/* @__PURE__ */ new Set()) : runRepositoryGit(git, directory, ["rev-list", "--stdin"], {
|
|
6350
6991
|
input: `${[...oids, ...tips.map((tip) => `^${tip}`)].join(`
|
|
6351
6992
|
`)}
|
|
6352
6993
|
`,
|
|
6353
6994
|
maxOutputBytes: 64 * 1048576
|
|
6354
6995
|
}).pipe(
|
|
6355
|
-
|
|
6996
|
+
Effect48.map((output) => {
|
|
6356
6997
|
let candidates = new Set(oids);
|
|
6357
6998
|
return new Set(
|
|
6358
6999
|
output.split(`
|
|
@@ -6363,10 +7004,10 @@ function readOrphaned(git, directory, oids, tips) {
|
|
|
6363
7004
|
}
|
|
6364
7005
|
function resetToCommit(git, command) {
|
|
6365
7006
|
let { worktreePath, target, mode, expectedHead, discard } = command;
|
|
6366
|
-
return
|
|
7007
|
+
return Effect48.gen(function* () {
|
|
6367
7008
|
let head = yield* readRefTarget(git, worktreePath, "HEAD");
|
|
6368
7009
|
if (head !== expectedHead)
|
|
6369
|
-
return yield*
|
|
7010
|
+
return yield* Effect48.fail({
|
|
6370
7011
|
_tag: "HeadMoved",
|
|
6371
7012
|
head: head ?? null
|
|
6372
7013
|
});
|
|
@@ -6375,7 +7016,7 @@ function resetToCommit(git, command) {
|
|
|
6375
7016
|
worktreePath,
|
|
6376
7017
|
`${target}^{commit}`
|
|
6377
7018
|
);
|
|
6378
|
-
return commit === void 0 ? yield*
|
|
7019
|
+
return commit === void 0 ? yield* Effect48.fail({
|
|
6379
7020
|
_tag: "RefMissing",
|
|
6380
7021
|
name: target
|
|
6381
7022
|
}) : (mode === "hard" && (yield* requireDiscardConfirmed(git, worktreePath, commit, discard)), yield* runRepositoryGit(
|
|
@@ -6387,7 +7028,7 @@ function resetToCommit(git, command) {
|
|
|
6387
7028
|
});
|
|
6388
7029
|
}
|
|
6389
7030
|
function requireDiscardConfirmed(git, directory, target, discard) {
|
|
6390
|
-
return
|
|
7031
|
+
return Effect48.gen(function* () {
|
|
6391
7032
|
let records = (yield* runRepositoryGit(
|
|
6392
7033
|
git,
|
|
6393
7034
|
directory,
|
|
@@ -6409,7 +7050,7 @@ function requireDiscardConfirmed(git, directory, target, discard) {
|
|
|
6409
7050
|
if (paths.length === 0) return;
|
|
6410
7051
|
let fingerprint2 = createHash3("sha256").update(paths.join("\0")).digest("hex");
|
|
6411
7052
|
if (discard !== fingerprint2)
|
|
6412
|
-
return yield*
|
|
7053
|
+
return yield* Effect48.fail({
|
|
6413
7054
|
_tag: "ResetDiscardsChanges",
|
|
6414
7055
|
paths: paths.slice(0, listedDiscards),
|
|
6415
7056
|
count: paths.length,
|
|
@@ -6424,7 +7065,7 @@ function readOverwrittenUntracked(git, directory, target, untracked) {
|
|
|
6424
7065
|
["diff", "--name-only", "-z", "--no-renames", "HEAD", target],
|
|
6425
7066
|
{ maxOutputBytes: 64 * 1048576 }
|
|
6426
7067
|
).pipe(
|
|
6427
|
-
|
|
7068
|
+
Effect48.map((output) => {
|
|
6428
7069
|
let changed = new Set(output.split("\0"));
|
|
6429
7070
|
return untracked.filter((path) => changed.has(path));
|
|
6430
7071
|
})
|
|
@@ -6432,7 +7073,7 @@ function readOverwrittenUntracked(git, directory, target, untracked) {
|
|
|
6432
7073
|
}
|
|
6433
7074
|
|
|
6434
7075
|
// src/apps/server/features/repository-refs/repository-refs.feature.ts
|
|
6435
|
-
import { Effect as
|
|
7076
|
+
import { Effect as Effect56 } from "effect";
|
|
6436
7077
|
|
|
6437
7078
|
// src/apps/server/features/repository-refs/git/branches/branch-failures.ts
|
|
6438
7079
|
function branchWriteFailed(error, name) {
|
|
@@ -6449,7 +7090,7 @@ function branchWriteFailed(error, name) {
|
|
|
6449
7090
|
}
|
|
6450
7091
|
|
|
6451
7092
|
// src/apps/server/features/repository-refs/git/branches/branch-git.ts
|
|
6452
|
-
import { Effect as
|
|
7093
|
+
import { Effect as Effect49 } from "effect";
|
|
6453
7094
|
|
|
6454
7095
|
// src/apps/server/features/repository-refs/git/parse-for-each-ref.ts
|
|
6455
7096
|
var forEachRefFormat = [
|
|
@@ -6530,7 +7171,12 @@ function tagFromRecord(record) {
|
|
|
6530
7171
|
if (!record.name.startsWith(tagPrefix))
|
|
6531
7172
|
return;
|
|
6532
7173
|
let target = record.objectType === "commit" ? record.commit : record.peeledObjectType === "commit" ? record.peeledObject : void 0;
|
|
6533
|
-
|
|
7174
|
+
if (target !== void 0)
|
|
7175
|
+
return {
|
|
7176
|
+
name: record.name.slice(tagPrefix.length),
|
|
7177
|
+
target,
|
|
7178
|
+
...record.objectType === "tag" ? { object: record.commit } : {}
|
|
7179
|
+
};
|
|
6534
7180
|
}
|
|
6535
7181
|
function parseTracking(track) {
|
|
6536
7182
|
return {
|
|
@@ -6553,20 +7199,20 @@ function requireValidBranchName(git, directory, name) {
|
|
|
6553
7199
|
_tag: "InvalidBranchName",
|
|
6554
7200
|
name
|
|
6555
7201
|
};
|
|
6556
|
-
return name.startsWith("-") || name === "HEAD" ?
|
|
7202
|
+
return name.startsWith("-") || name === "HEAD" ? Effect49.fail(invalid) : requireValidRefName(git, directory, branchRef(name), invalid);
|
|
6557
7203
|
}
|
|
6558
7204
|
function readBranchTarget(git, directory, name) {
|
|
6559
7205
|
return readRefTarget(git, directory, `${branchRef(name)}^{commit}`);
|
|
6560
7206
|
}
|
|
6561
7207
|
function requireBranchTarget(git, directory, name, expectedTarget) {
|
|
6562
7208
|
return readBranchTarget(git, directory, name).pipe(
|
|
6563
|
-
|
|
7209
|
+
Effect49.flatMap((target) => target === void 0 ? Effect49.fail({
|
|
6564
7210
|
_tag: "RefMissing",
|
|
6565
7211
|
name
|
|
6566
|
-
}) : expectedTarget !== void 0 && target !== expectedTarget ?
|
|
7212
|
+
}) : expectedTarget !== void 0 && target !== expectedTarget ? Effect49.fail({
|
|
6567
7213
|
_tag: "BranchMoved",
|
|
6568
7214
|
name
|
|
6569
|
-
}) :
|
|
7215
|
+
}) : Effect49.succeed(target))
|
|
6570
7216
|
);
|
|
6571
7217
|
}
|
|
6572
7218
|
function worktreeHolding(worktrees, name) {
|
|
@@ -6579,7 +7225,7 @@ function readLocalBranch(git, directory, worktrees, name) {
|
|
|
6579
7225
|
["for-each-ref", `--format=${forEachRefFormat}`, branchRef(name)],
|
|
6580
7226
|
refCommand
|
|
6581
7227
|
).pipe(
|
|
6582
|
-
|
|
7228
|
+
Effect49.map((output) => {
|
|
6583
7229
|
let record = parseForEachRef(output).find(
|
|
6584
7230
|
(candidate) => candidate.name === branchRef(name)
|
|
6585
7231
|
), { worktreePath: _raw, ...branch } = (record === void 0 ? void 0 : localBranchFromRecord(record)) ?? { name }, holder = worktreeHolding(worktrees, name);
|
|
@@ -6602,19 +7248,19 @@ function requireRemoteBranch(git, directory, upstream) {
|
|
|
6602
7248
|
["rev-parse", "--verify", "--quiet", `refs/remotes/${name}`],
|
|
6603
7249
|
refCommand
|
|
6604
7250
|
).pipe(
|
|
6605
|
-
|
|
7251
|
+
Effect49.mapError(
|
|
6606
7252
|
(error) => isGitRejection(error) ? { _tag: "RefMissing", name } : branchWriteFailed(error, name)
|
|
6607
7253
|
),
|
|
6608
|
-
|
|
7254
|
+
Effect49.asVoid
|
|
6609
7255
|
);
|
|
6610
7256
|
}
|
|
6611
7257
|
|
|
6612
7258
|
// src/apps/server/features/repository-refs/git/branches/delete-branch.ts
|
|
6613
|
-
import { Effect as
|
|
7259
|
+
import { Effect as Effect50 } from "effect";
|
|
6614
7260
|
var listedCommits = 20, pushCommand = { literalPathspecs: !1, timeoutMilliseconds: 12e4 };
|
|
6615
7261
|
function deleteBranch(git, access, command) {
|
|
6616
7262
|
let { force, local, remote, worktreePath } = command;
|
|
6617
|
-
return
|
|
7263
|
+
return Effect50.gen(function* () {
|
|
6618
7264
|
return local !== void 0 && (yield* requireDeletableLocal(git, access, worktreePath, local)), remote !== void 0 && (yield* requireRemoteTarget(git, worktreePath, remote)), force || (yield* rejectUnmergedCommits(git, worktreePath, command)), remote !== void 0 && (yield* deleteRemote(git, worktreePath, remote)), local !== void 0 && (yield* deleteLocal(git, worktreePath, local)), {
|
|
6619
7265
|
...local === void 0 ? {} : { local },
|
|
6620
7266
|
...remote === void 0 ? {} : { remote }
|
|
@@ -6622,10 +7268,10 @@ function deleteBranch(git, access, command) {
|
|
|
6622
7268
|
});
|
|
6623
7269
|
}
|
|
6624
7270
|
function requireDeletableLocal(git, access, worktreePath, { name, target }) {
|
|
6625
|
-
return
|
|
7271
|
+
return Effect50.gen(function* () {
|
|
6626
7272
|
let holder = worktreeHolding(yield* access.worktrees(worktreePath), name);
|
|
6627
7273
|
if (holder !== void 0)
|
|
6628
|
-
return yield*
|
|
7274
|
+
return yield* Effect50.fail({
|
|
6629
7275
|
_tag: "BranchCheckedOutElsewhere",
|
|
6630
7276
|
name,
|
|
6631
7277
|
worktreePath: holder.path
|
|
@@ -6640,14 +7286,14 @@ function deleteLocal(git, directory, { name, target }) {
|
|
|
6640
7286
|
["update-ref", "-d", branchRef(name), target],
|
|
6641
7287
|
refCommand
|
|
6642
7288
|
).pipe(
|
|
6643
|
-
|
|
7289
|
+
Effect50.catchIf(
|
|
6644
7290
|
isGitRejection,
|
|
6645
|
-
() =>
|
|
7291
|
+
() => Effect50.fail({
|
|
6646
7292
|
_tag: "BranchMoved",
|
|
6647
7293
|
name
|
|
6648
7294
|
})
|
|
6649
7295
|
),
|
|
6650
|
-
|
|
7296
|
+
Effect50.andThen(
|
|
6651
7297
|
runRepositoryGit(
|
|
6652
7298
|
git,
|
|
6653
7299
|
directory,
|
|
@@ -6655,7 +7301,7 @@ function deleteLocal(git, directory, { name, target }) {
|
|
|
6655
7301
|
{ ...refCommand, exitCodes: [0, 1, 128] }
|
|
6656
7302
|
)
|
|
6657
7303
|
),
|
|
6658
|
-
|
|
7304
|
+
Effect50.asVoid
|
|
6659
7305
|
);
|
|
6660
7306
|
}
|
|
6661
7307
|
function requireRemoteTarget(git, directory, remote) {
|
|
@@ -6666,12 +7312,12 @@ function requireRemoteTarget(git, directory, remote) {
|
|
|
6666
7312
|
["rev-parse", "--verify", "--quiet", `refs/remotes/${name}^{commit}`],
|
|
6667
7313
|
{ ...refCommand, exitCodes: [0, 1] }
|
|
6668
7314
|
).pipe(
|
|
6669
|
-
|
|
7315
|
+
Effect50.flatMap((output) => {
|
|
6670
7316
|
let target = output.trim();
|
|
6671
|
-
return target.length === 0 ?
|
|
7317
|
+
return target.length === 0 ? Effect50.fail({
|
|
6672
7318
|
_tag: "RefMissing",
|
|
6673
7319
|
name
|
|
6674
|
-
}) : target === remote.target ?
|
|
7320
|
+
}) : target === remote.target ? Effect50.void : Effect50.fail({
|
|
6675
7321
|
_tag: "BranchMoved",
|
|
6676
7322
|
name
|
|
6677
7323
|
});
|
|
@@ -6690,7 +7336,7 @@ function rejectUnmergedCommits(git, directory, { local, remote }) {
|
|
|
6690
7336
|
"--remotes",
|
|
6691
7337
|
"--tags"
|
|
6692
7338
|
];
|
|
6693
|
-
return
|
|
7339
|
+
return Effect50.all(
|
|
6694
7340
|
{
|
|
6695
7341
|
count: runRepositoryGit(
|
|
6696
7342
|
git,
|
|
@@ -6712,9 +7358,9 @@ function rejectUnmergedCommits(git, directory, { local, remote }) {
|
|
|
6712
7358
|
},
|
|
6713
7359
|
{ concurrency: "unbounded" }
|
|
6714
7360
|
).pipe(
|
|
6715
|
-
|
|
7361
|
+
Effect50.flatMap(({ count, log }) => {
|
|
6716
7362
|
let unmerged = Number.parseInt(count.trim(), 10);
|
|
6717
|
-
return unmerged === 0 ?
|
|
7363
|
+
return unmerged === 0 ? Effect50.void : Effect50.fail({
|
|
6718
7364
|
_tag: "BranchNotMerged",
|
|
6719
7365
|
commits: parseCommits(log),
|
|
6720
7366
|
count: unmerged,
|
|
@@ -6736,8 +7382,8 @@ function deleteRemote(git, directory, remote) {
|
|
|
6736
7382
|
],
|
|
6737
7383
|
pushCommand
|
|
6738
7384
|
).pipe(
|
|
6739
|
-
|
|
6740
|
-
|
|
7385
|
+
Effect50.asVoid,
|
|
7386
|
+
Effect50.mapError(
|
|
6741
7387
|
(error) => isGitRejection(error) && /stale info|fetch first/i.test(error.detail) ? { _tag: "BranchMoved", name: remoteLabel(remote) } : branchWriteFailed(error, remoteLabel(remote))
|
|
6742
7388
|
)
|
|
6743
7389
|
);
|
|
@@ -6757,10 +7403,10 @@ function isDefined(value) {
|
|
|
6757
7403
|
}
|
|
6758
7404
|
|
|
6759
7405
|
// src/apps/server/features/repository-refs/git/branches/rename-branch.ts
|
|
6760
|
-
import { Effect as
|
|
7406
|
+
import { Effect as Effect51 } from "effect";
|
|
6761
7407
|
function renameBranch(git, access, command) {
|
|
6762
7408
|
let { name, newName, worktreePath } = command;
|
|
6763
|
-
return
|
|
7409
|
+
return Effect51.gen(function* () {
|
|
6764
7410
|
yield* requireValidBranchName(git, worktreePath, newName);
|
|
6765
7411
|
let before = yield* access.worktrees(worktreePath);
|
|
6766
7412
|
yield* rejectHeldElsewhere(before, name, worktreePath), yield* requireRenameSource(git, command, before), yield* runRepositoryGit(
|
|
@@ -6768,17 +7414,17 @@ function renameBranch(git, access, command) {
|
|
|
6768
7414
|
worktreePath,
|
|
6769
7415
|
["branch", "-m", name, newName],
|
|
6770
7416
|
refCommand
|
|
6771
|
-
).pipe(
|
|
7417
|
+
).pipe(Effect51.mapError((error) => branchWriteFailed(error, newName)));
|
|
6772
7418
|
let after = yield* access.worktrees(worktreePath);
|
|
6773
7419
|
return { branch: yield* readLocalBranch(git, worktreePath, after, newName), previousName: name };
|
|
6774
7420
|
});
|
|
6775
7421
|
}
|
|
6776
7422
|
function requireRenameSource(git, { expectedTarget, name, worktreePath }, worktrees) {
|
|
6777
7423
|
return readBranchTarget(git, worktreePath, name).pipe(
|
|
6778
|
-
|
|
7424
|
+
Effect51.flatMap((target) => target !== void 0 ? target === expectedTarget ? Effect51.void : Effect51.fail({
|
|
6779
7425
|
_tag: "BranchMoved",
|
|
6780
7426
|
name
|
|
6781
|
-
}) : expectedTarget === void 0 && worktreeHolding(worktrees, name)?.path === worktreePath ?
|
|
7427
|
+
}) : expectedTarget === void 0 && worktreeHolding(worktrees, name)?.path === worktreePath ? Effect51.void : Effect51.fail({
|
|
6782
7428
|
_tag: "RefMissing",
|
|
6783
7429
|
name
|
|
6784
7430
|
}))
|
|
@@ -6786,7 +7432,7 @@ function requireRenameSource(git, { expectedTarget, name, worktreePath }, worktr
|
|
|
6786
7432
|
}
|
|
6787
7433
|
function rejectHeldElsewhere(worktrees, name, worktreePath) {
|
|
6788
7434
|
let holder = worktreeHolding(worktrees, name);
|
|
6789
|
-
return holder === void 0 || holder.path === worktreePath ?
|
|
7435
|
+
return holder === void 0 || holder.path === worktreePath ? Effect51.void : Effect51.fail({
|
|
6790
7436
|
_tag: "BranchCheckedOutElsewhere",
|
|
6791
7437
|
name,
|
|
6792
7438
|
worktreePath: holder.path
|
|
@@ -6795,13 +7441,13 @@ function rejectHeldElsewhere(worktrees, name, worktreePath) {
|
|
|
6795
7441
|
|
|
6796
7442
|
// src/apps/server/features/repository-refs/git/checkout-repository-ref.ts
|
|
6797
7443
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
6798
|
-
import { Effect as
|
|
7444
|
+
import { Effect as Effect52 } from "effect";
|
|
6799
7445
|
var checkoutCommand = {
|
|
6800
7446
|
literalPathspecs: !1,
|
|
6801
7447
|
timeoutMilliseconds: 6e4
|
|
6802
7448
|
};
|
|
6803
7449
|
function checkoutRepositoryRef(git, access, command) {
|
|
6804
|
-
return
|
|
7450
|
+
return Effect52.gen(function* () {
|
|
6805
7451
|
let worktrees = yield* access.worktrees(command.worktreePath), worktree = yield* requireWorktree(worktrees, command.worktreePath), target = yield* resolveTarget(git, worktree.path, command.target);
|
|
6806
7452
|
if (yield* rejectBranchCheckedOutElsewhere(worktrees, worktree, target), target._tag === "LocalBranch" && worktree.head.branch === target.name)
|
|
6807
7453
|
return {
|
|
@@ -6809,18 +7455,18 @@ function checkoutRepositoryRef(git, access, command) {
|
|
|
6809
7455
|
stash: "none",
|
|
6810
7456
|
worktreePath: worktree.path
|
|
6811
7457
|
};
|
|
6812
|
-
let stash = yield*
|
|
7458
|
+
let stash = yield* Effect52.uninterruptible(
|
|
6813
7459
|
checkoutWithAutoStash(git, worktree.path, target)
|
|
6814
7460
|
);
|
|
6815
7461
|
return { head: yield* readCheckedOutHead(access, worktree.path), stash, worktreePath: worktree.path };
|
|
6816
7462
|
});
|
|
6817
7463
|
}
|
|
6818
7464
|
function checkoutWithAutoStash(git, directory, target) {
|
|
6819
|
-
return
|
|
7465
|
+
return Effect52.gen(function* () {
|
|
6820
7466
|
let stash = yield* stashLocalChanges(git, directory, target);
|
|
6821
7467
|
return stash === void 0 ? (yield* runCheckout(git, directory, target), "none") : (yield* runCheckout(git, directory, target).pipe(
|
|
6822
|
-
|
|
6823
|
-
() => restoreStash(git, directory, stash).pipe(
|
|
7468
|
+
Effect52.tapError(
|
|
7469
|
+
() => restoreStash(git, directory, stash).pipe(Effect52.ignore)
|
|
6824
7470
|
)
|
|
6825
7471
|
), (yield* restoreStash(git, directory, stash)) ? "restored" : "kept");
|
|
6826
7472
|
});
|
|
@@ -6829,15 +7475,15 @@ function requireWorktree(worktrees, worktreePath) {
|
|
|
6829
7475
|
let worktree = worktrees.find(
|
|
6830
7476
|
(candidate) => candidate.path === worktreePath
|
|
6831
7477
|
);
|
|
6832
|
-
return worktree === void 0 ?
|
|
7478
|
+
return worktree === void 0 ? Effect52.fail(
|
|
6833
7479
|
repositoryRejected(
|
|
6834
7480
|
"Missing",
|
|
6835
7481
|
"This worktree does not belong to the repository."
|
|
6836
7482
|
)
|
|
6837
|
-
) :
|
|
7483
|
+
) : Effect52.succeed(worktree);
|
|
6838
7484
|
}
|
|
6839
7485
|
function resolveTarget(git, directory, target) {
|
|
6840
|
-
return target._tag !== "RemoteBranch" ?
|
|
7486
|
+
return target._tag !== "RemoteBranch" ? Effect52.succeed(target) : Effect52.gen(function* () {
|
|
6841
7487
|
if (!(yield* gitAccepts(git, directory, [
|
|
6842
7488
|
"show-ref",
|
|
6843
7489
|
"--verify",
|
|
@@ -6850,8 +7496,8 @@ function resolveTarget(git, directory, target) {
|
|
|
6850
7496
|
["rev-parse", "--abbrev-ref", "--quiet", `${target.name}@{upstream}`],
|
|
6851
7497
|
checkoutCommand
|
|
6852
7498
|
).pipe(
|
|
6853
|
-
|
|
6854
|
-
|
|
7499
|
+
Effect52.map((upstream) => upstream.trim()),
|
|
7500
|
+
Effect52.catchIf(isGitRejection, () => Effect52.succeed(""))
|
|
6855
7501
|
);
|
|
6856
7502
|
return tracked.length === 0 || tracked === `${target.remote}/${target.name}` ? { _tag: "LocalBranch", name: target.name } : {
|
|
6857
7503
|
_tag: "DetachedRemoteBranch",
|
|
@@ -6861,25 +7507,25 @@ function resolveTarget(git, directory, target) {
|
|
|
6861
7507
|
});
|
|
6862
7508
|
}
|
|
6863
7509
|
function rejectBranchCheckedOutElsewhere(worktrees, worktree, target) {
|
|
6864
|
-
if (target._tag !== "LocalBranch") return
|
|
7510
|
+
if (target._tag !== "LocalBranch") return Effect52.void;
|
|
6865
7511
|
let elsewhere = worktrees.find(
|
|
6866
7512
|
(candidate) => candidate.path !== worktree.path && candidate.head.branch === target.name
|
|
6867
7513
|
);
|
|
6868
|
-
return elsewhere === void 0 ?
|
|
7514
|
+
return elsewhere === void 0 ? Effect52.void : Effect52.fail({
|
|
6869
7515
|
_tag: "BranchCheckedOutElsewhere",
|
|
6870
7516
|
name: target.name,
|
|
6871
7517
|
worktreePath: elsewhere.path
|
|
6872
7518
|
});
|
|
6873
7519
|
}
|
|
6874
7520
|
function stashLocalChanges(git, directory, target) {
|
|
6875
|
-
return
|
|
7521
|
+
return Effect52.gen(function* () {
|
|
6876
7522
|
if ((yield* runRepositoryGit(
|
|
6877
7523
|
git,
|
|
6878
7524
|
directory,
|
|
6879
7525
|
["status", "--porcelain", "-z"],
|
|
6880
7526
|
checkoutCommand
|
|
6881
|
-
).pipe(
|
|
6882
|
-
let token = `rebase-auto-stash:${randomUUID5()}`,
|
|
7527
|
+
).pipe(Effect52.mapError((error) => checkoutFailure(error, target.name)))).length === 0) return;
|
|
7528
|
+
let token = `rebase-auto-stash:${randomUUID5()}`, stashed = yield* runRepositoryGit(
|
|
6883
7529
|
git,
|
|
6884
7530
|
directory,
|
|
6885
7531
|
[
|
|
@@ -6891,12 +7537,11 @@ function stashLocalChanges(git, directory, target) {
|
|
|
6891
7537
|
],
|
|
6892
7538
|
checkoutCommand
|
|
6893
7539
|
).pipe(
|
|
6894
|
-
|
|
6895
|
-
|
|
7540
|
+
Effect52.as(!0),
|
|
7541
|
+
Effect52.catchIf(isGitRejection, () => Effect52.succeed(!1))
|
|
6896
7542
|
), entry = yield* findStash(git, directory, token);
|
|
6897
|
-
return
|
|
7543
|
+
return !stashed || entry === void 0 ? yield* Effect52.fail({
|
|
6898
7544
|
_tag: "CheckoutRejected",
|
|
6899
|
-
detail: stashFailure,
|
|
6900
7545
|
reason: "StashFailed"
|
|
6901
7546
|
}) : token;
|
|
6902
7547
|
});
|
|
@@ -6908,7 +7553,7 @@ function findStash(git, directory, token) {
|
|
|
6908
7553
|
["stash", "list", "--format=%H%x00%s"],
|
|
6909
7554
|
checkoutCommand
|
|
6910
7555
|
).pipe(
|
|
6911
|
-
|
|
7556
|
+
Effect52.map((listed) => {
|
|
6912
7557
|
let lines = listed.split(`
|
|
6913
7558
|
`), index2 = lines.findIndex(
|
|
6914
7559
|
(line) => line.split("\0")[1]?.includes(token)
|
|
@@ -6924,8 +7569,8 @@ function runCheckout(git, directory, target) {
|
|
|
6924
7569
|
checkoutArguments(target),
|
|
6925
7570
|
checkoutCommand
|
|
6926
7571
|
).pipe(
|
|
6927
|
-
|
|
6928
|
-
|
|
7572
|
+
Effect52.asVoid,
|
|
7573
|
+
Effect52.mapError((error) => checkoutFailure(error, target.name))
|
|
6929
7574
|
);
|
|
6930
7575
|
}
|
|
6931
7576
|
function checkoutArguments(target) {
|
|
@@ -6951,7 +7596,7 @@ function checkoutArguments(target) {
|
|
|
6951
7596
|
}
|
|
6952
7597
|
}
|
|
6953
7598
|
function restoreStash(git, directory, token) {
|
|
6954
|
-
return
|
|
7599
|
+
return Effect52.gen(function* () {
|
|
6955
7600
|
let entry = yield* findStash(git, directory, token);
|
|
6956
7601
|
if (entry === void 0 || !(yield* gitAccepts(git, directory, [
|
|
6957
7602
|
"stash",
|
|
@@ -6968,14 +7613,14 @@ function restoreStash(git, directory, token) {
|
|
|
6968
7613
|
}
|
|
6969
7614
|
function readCheckedOutHead(access, worktreePath) {
|
|
6970
7615
|
return access.worktrees(worktreePath).pipe(
|
|
6971
|
-
|
|
6972
|
-
|
|
7616
|
+
Effect52.flatMap((worktrees) => requireWorktree(worktrees, worktreePath)),
|
|
7617
|
+
Effect52.map((worktree) => worktree.head)
|
|
6973
7618
|
);
|
|
6974
7619
|
}
|
|
6975
7620
|
function gitAccepts(git, directory, args) {
|
|
6976
7621
|
return runRepositoryGit(git, directory, args, checkoutCommand).pipe(
|
|
6977
|
-
|
|
6978
|
-
|
|
7622
|
+
Effect52.as(!0),
|
|
7623
|
+
Effect52.catchIf(isGitRejection, () => Effect52.succeed(!1))
|
|
6979
7624
|
);
|
|
6980
7625
|
}
|
|
6981
7626
|
function checkoutFailure(error, targetName) {
|
|
@@ -6990,17 +7635,16 @@ function checkoutFailure(error, targetName) {
|
|
|
6990
7635
|
error.detail
|
|
6991
7636
|
) ? { _tag: "RefMissing", name: targetName } : /would be overwritten by checkout/i.test(error.detail) ? {
|
|
6992
7637
|
_tag: "CheckoutRejected",
|
|
6993
|
-
detail: error.detail,
|
|
6994
7638
|
reason: "LocalChanges"
|
|
6995
7639
|
} : repositoryRejected("GitFailed", error.detail);
|
|
6996
7640
|
}
|
|
6997
7641
|
|
|
6998
7642
|
// src/apps/server/features/repository-refs/git/read-repository-refs.ts
|
|
6999
|
-
import { Effect as
|
|
7643
|
+
import { Effect as Effect53 } from "effect";
|
|
7000
7644
|
var readTimeoutMilliseconds = 15e3, maximumRefsOutputBytes2 = 16 * 1048576;
|
|
7001
7645
|
function readRepositoryRefs(git, repository) {
|
|
7002
|
-
return
|
|
7003
|
-
let output = yield*
|
|
7646
|
+
return Effect53.gen(function* () {
|
|
7647
|
+
let output = yield* Effect53.all(
|
|
7004
7648
|
{
|
|
7005
7649
|
branches: listRefs(
|
|
7006
7650
|
git,
|
|
@@ -7051,7 +7695,7 @@ function listRefs(git, directory, pattern, sort) {
|
|
|
7051
7695
|
maxOutputBytes: maximumRefsOutputBytes2,
|
|
7052
7696
|
timeoutMilliseconds: readTimeoutMilliseconds
|
|
7053
7697
|
}
|
|
7054
|
-
).pipe(
|
|
7698
|
+
).pipe(Effect53.map(parseForEachRef));
|
|
7055
7699
|
}
|
|
7056
7700
|
function canonicalizeBranchWorktrees(branches, rawWorktrees, canonicalWorktrees) {
|
|
7057
7701
|
let canonicalByRawPath = new Map(
|
|
@@ -7080,12 +7724,12 @@ function readRemoteMetadata(git, directory) {
|
|
|
7080
7724
|
["config", "--get-regexp", "^remote\\..*\\.url$"],
|
|
7081
7725
|
{ timeoutMilliseconds: 5e3, maxOutputBytes: 65536 }
|
|
7082
7726
|
).pipe(
|
|
7083
|
-
|
|
7727
|
+
Effect53.map((remotes) => ({
|
|
7084
7728
|
githubRepository: githubRepositoryFromRemotes(remotes),
|
|
7085
7729
|
remoteProviders: remoteProvidersFromConfig(remotes)
|
|
7086
7730
|
})),
|
|
7087
|
-
|
|
7088
|
-
() =>
|
|
7731
|
+
Effect53.catch(
|
|
7732
|
+
() => Effect53.succeed({ githubRepository: void 0, remoteProviders: [] })
|
|
7089
7733
|
)
|
|
7090
7734
|
);
|
|
7091
7735
|
}
|
|
@@ -7146,88 +7790,165 @@ function fitRepositoryRefs(refs) {
|
|
|
7146
7790
|
}
|
|
7147
7791
|
|
|
7148
7792
|
// src/apps/server/features/repository-refs/git/repository-tags.ts
|
|
7149
|
-
import { Effect as
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7793
|
+
import { Effect as Effect54 } from "effect";
|
|
7794
|
+
var pushCommand2 = { literalPathspecs: !1, timeoutMilliseconds: 12e4 };
|
|
7795
|
+
function createTag(git, { name, target, message, worktreePath }) {
|
|
7796
|
+
return Effect54.gen(function* () {
|
|
7797
|
+
if (yield* requireValidTagName(git, worktreePath, name), message === void 0)
|
|
7798
|
+
return yield* requireUnsignedTagsAllowed(git, worktreePath), yield* runRepositoryGit(
|
|
7799
|
+
git,
|
|
7800
|
+
worktreePath,
|
|
7801
|
+
["tag", "--no-sign", name, target],
|
|
7802
|
+
refCommand
|
|
7803
|
+
).pipe(Effect54.mapError(tagWriteFailed)), { name, target };
|
|
7804
|
+
yield* runRepositoryGit(
|
|
7153
7805
|
git,
|
|
7154
7806
|
worktreePath,
|
|
7155
|
-
["tag", "--
|
|
7156
|
-
refCommand
|
|
7157
|
-
).pipe(
|
|
7807
|
+
["tag", "--annotate", "--file=-", name, target],
|
|
7808
|
+
{ ...refCommand, input: message }
|
|
7809
|
+
).pipe(Effect54.mapError(tagWriteFailed));
|
|
7810
|
+
let object = yield* readRefTarget(git, worktreePath, tagRef(name));
|
|
7811
|
+
return object === void 0 ? { name, target } : { name, target, object };
|
|
7158
7812
|
});
|
|
7159
7813
|
}
|
|
7160
|
-
function deleteTag(git, { name, worktreePath }) {
|
|
7161
|
-
return
|
|
7162
|
-
|
|
7163
|
-
|
|
7814
|
+
function deleteTag(git, { name, local, remote, worktreePath }) {
|
|
7815
|
+
return Effect54.gen(function* () {
|
|
7816
|
+
return local !== void 0 && (yield* requireTagObject(git, worktreePath, name)), remote !== void 0 && (yield* runRepositoryGit(
|
|
7817
|
+
git,
|
|
7818
|
+
worktreePath,
|
|
7819
|
+
[
|
|
7820
|
+
"push",
|
|
7821
|
+
`--force-with-lease=${tagRef(name)}:${remote.object}`,
|
|
7822
|
+
remote.remote,
|
|
7823
|
+
"--delete",
|
|
7824
|
+
tagRef(name)
|
|
7825
|
+
],
|
|
7826
|
+
pushCommand2
|
|
7827
|
+
).pipe(
|
|
7828
|
+
Effect54.catchIf(
|
|
7829
|
+
(error) => isGitRejection(error) && /stale info/.test(error.detail),
|
|
7830
|
+
() => Effect54.fail(tagRejected("RemoteDiffers"))
|
|
7831
|
+
)
|
|
7832
|
+
)), local !== void 0 && (yield* runRepositoryGit(
|
|
7164
7833
|
git,
|
|
7165
7834
|
worktreePath,
|
|
7166
|
-
["
|
|
7835
|
+
["update-ref", "-d", tagRef(name), local.object],
|
|
7167
7836
|
refCommand
|
|
7168
|
-
)
|
|
7837
|
+
).pipe(
|
|
7838
|
+
Effect54.catchIf(isGitRejection, () => Effect54.fail(tagRejected("Moved")))
|
|
7839
|
+
)), { name };
|
|
7169
7840
|
});
|
|
7170
7841
|
}
|
|
7842
|
+
function readTagAnnotation(git, { name, worktreePath }) {
|
|
7843
|
+
return runRepositoryGit(
|
|
7844
|
+
git,
|
|
7845
|
+
worktreePath,
|
|
7846
|
+
[
|
|
7847
|
+
"for-each-ref",
|
|
7848
|
+
"--count=1",
|
|
7849
|
+
"--sort=refname",
|
|
7850
|
+
`--format=${[
|
|
7851
|
+
"%(refname)",
|
|
7852
|
+
"%(objecttype)",
|
|
7853
|
+
"%(objectname)",
|
|
7854
|
+
"%(*objectname)",
|
|
7855
|
+
"%(taggername)",
|
|
7856
|
+
"%(taggerdate:iso-strict)",
|
|
7857
|
+
"%(contents:signature)",
|
|
7858
|
+
"%(contents)"
|
|
7859
|
+
].join("%00")}`,
|
|
7860
|
+
tagRef(name)
|
|
7861
|
+
],
|
|
7862
|
+
refCommand
|
|
7863
|
+
).pipe(
|
|
7864
|
+
Effect54.flatMap((output) => {
|
|
7865
|
+
let record = output.replace(/\n$/, "").split("\0"), [refname, type, object, target, tagger = "", date = ""] = record, signature = record[6] ?? "", contents = record.slice(7).join("\0");
|
|
7866
|
+
return refname !== tagRef(name) || type !== "tag" || object === void 0 || target === void 0 ? Effect54.fail({ _tag: "RefMissing", name }) : Effect54.succeed({
|
|
7867
|
+
name,
|
|
7868
|
+
object,
|
|
7869
|
+
target,
|
|
7870
|
+
tagger: { name: tagger.slice(0, 512), date: date.slice(0, 64) },
|
|
7871
|
+
message: (contents.endsWith(signature) && signature.length > 0 ? contents.slice(0, -signature.length) : contents).trimEnd().slice(0, 65536),
|
|
7872
|
+
signed: signature.length > 0
|
|
7873
|
+
});
|
|
7874
|
+
})
|
|
7875
|
+
);
|
|
7876
|
+
}
|
|
7171
7877
|
function requireValidTagName(git, directory, name) {
|
|
7172
|
-
let invalid =
|
|
7173
|
-
return name.startsWith("-") ?
|
|
7878
|
+
let invalid = tagRejected("InvalidName");
|
|
7879
|
+
return name.startsWith("-") ? Effect54.fail(invalid) : requireValidRefName(git, directory, tagRef(name), invalid);
|
|
7174
7880
|
}
|
|
7175
|
-
function
|
|
7176
|
-
return
|
|
7177
|
-
|
|
7178
|
-
|
|
7881
|
+
function requireUnsignedTagsAllowed(git, directory) {
|
|
7882
|
+
return runRepositoryGit(
|
|
7883
|
+
git,
|
|
7884
|
+
directory,
|
|
7885
|
+
["config", "--type=bool", "--get", "tag.gpgSign"],
|
|
7886
|
+
{ ...refCommand, exitCodes: [0, 1] }
|
|
7887
|
+
).pipe(
|
|
7888
|
+
Effect54.flatMap(
|
|
7889
|
+
(value) => value.trim() === "true" ? Effect54.fail(tagRejected("MessageRequired")) : Effect54.void
|
|
7890
|
+
)
|
|
7891
|
+
);
|
|
7892
|
+
}
|
|
7893
|
+
function requireTagObject(git, directory, name) {
|
|
7894
|
+
return readRefTarget(git, directory, tagRef(name)).pipe(
|
|
7895
|
+
Effect54.flatMap(
|
|
7896
|
+
(object) => object === void 0 ? Effect54.fail({ _tag: "RefMissing", name }) : Effect54.void
|
|
7179
7897
|
)
|
|
7180
7898
|
);
|
|
7181
7899
|
}
|
|
7182
7900
|
function tagRef(name) {
|
|
7183
7901
|
return `refs/tags/${name}`;
|
|
7184
7902
|
}
|
|
7903
|
+
function tagRejected(reason) {
|
|
7904
|
+
return { _tag: "TagRejected", reason };
|
|
7905
|
+
}
|
|
7185
7906
|
function tagWriteFailed(error) {
|
|
7186
|
-
return isGitRejection(error) && /already exists/.test(error.detail) ?
|
|
7907
|
+
return isGitRejection(error) && /already exists/.test(error.detail) ? tagRejected("Exists") : repositoryRejected("GitFailed", error.detail);
|
|
7187
7908
|
}
|
|
7188
7909
|
|
|
7189
7910
|
// src/apps/server/features/repository-refs/repository-change-publisher.ts
|
|
7190
|
-
import { Effect as
|
|
7911
|
+
import { Effect as Effect55, Exit, Queue as Queue3, Scope, Semaphore as Semaphore2 } from "effect";
|
|
7191
7912
|
var maximumWatchedRepositories = 32;
|
|
7192
7913
|
function acquireRepositoryChangePublisher(git, watcher, events) {
|
|
7193
|
-
return
|
|
7194
|
-
let scope = yield*
|
|
7914
|
+
return Effect55.gen(function* () {
|
|
7915
|
+
let scope = yield* Effect55.scope, mutex = yield* Semaphore2.make(1), watches = /* @__PURE__ */ new Map(), changed = /* @__PURE__ */ new Map(), pending = yield* Queue3.make({
|
|
7195
7916
|
capacity: 1,
|
|
7196
7917
|
strategy: "dropping"
|
|
7197
7918
|
}), closed = !1;
|
|
7198
|
-
yield*
|
|
7199
|
-
() =>
|
|
7919
|
+
yield* Effect55.addFinalizer(
|
|
7920
|
+
() => Effect55.sync(() => {
|
|
7200
7921
|
closed = !0, watches.clear(), changed.clear();
|
|
7201
7922
|
})
|
|
7202
7923
|
), yield* publishRepositoryChanges(pending, changed, events).pipe(
|
|
7203
|
-
|
|
7924
|
+
Effect55.forkScoped
|
|
7204
7925
|
);
|
|
7205
|
-
let register = (repositoryId, directory) =>
|
|
7926
|
+
let register = (repositoryId, directory) => Effect55.gen(function* () {
|
|
7206
7927
|
let oldest = watches.entries().next().value;
|
|
7207
7928
|
watches.size >= maximumWatchedRepositories && oldest !== void 0 && (watches.delete(oldest[0]), yield* Scope.close(oldest[1], Exit.void));
|
|
7208
7929
|
let owned = yield* Scope.fork(scope);
|
|
7209
|
-
yield*
|
|
7930
|
+
yield* Effect55.acquireRelease(
|
|
7210
7931
|
watcher.watch(directory, (kind) => {
|
|
7211
7932
|
changed.get(repositoryId) !== "Refs" && changed.set(repositoryId, kind), Queue3.offerUnsafe(pending, void 0);
|
|
7212
7933
|
}),
|
|
7213
|
-
(handle) =>
|
|
7214
|
-
).pipe(
|
|
7215
|
-
}).pipe(
|
|
7934
|
+
(handle) => Effect55.sync(handle.close)
|
|
7935
|
+
).pipe(Effect55.provideService(Scope.Scope, owned)), watches.set(repositoryId, owned);
|
|
7936
|
+
}).pipe(Effect55.uninterruptible);
|
|
7216
7937
|
return {
|
|
7217
|
-
watch: (repository) =>
|
|
7938
|
+
watch: (repository) => Effect55.gen(function* () {
|
|
7218
7939
|
if (closed || watches.has(repository.id)) return;
|
|
7219
7940
|
let directory = yield* readGitCommonDirectory(
|
|
7220
7941
|
git,
|
|
7221
7942
|
repository.path,
|
|
7222
7943
|
{ timeoutMilliseconds: 5e3 }
|
|
7223
|
-
).pipe(
|
|
7944
|
+
).pipe(Effect55.catch(() => Effect55.succeed(void 0)));
|
|
7224
7945
|
closed || directory === void 0 || (yield* register(repository.id, directory));
|
|
7225
7946
|
}).pipe(Semaphore2.withPermit(mutex))
|
|
7226
7947
|
};
|
|
7227
7948
|
});
|
|
7228
7949
|
}
|
|
7229
7950
|
function publishRepositoryChanges(pending, changed, events) {
|
|
7230
|
-
return
|
|
7951
|
+
return Effect55.gen(function* () {
|
|
7231
7952
|
for (; ; ) {
|
|
7232
7953
|
yield* Queue3.take(pending), yield* Queue3.clear(pending);
|
|
7233
7954
|
let batch = [...changed];
|
|
@@ -7253,16 +7974,16 @@ var branchPolicy = {
|
|
|
7253
7974
|
duringOperation: "proceed"
|
|
7254
7975
|
};
|
|
7255
7976
|
function repositoryRefsFeature(dependencies) {
|
|
7256
|
-
return
|
|
7257
|
-
let { access, git } = dependencies, { command } = repositoryRoutes(dependencies), changes = yield* acquireRepositoryChangePublisher(
|
|
7977
|
+
return Effect56.gen(function* () {
|
|
7978
|
+
let { access, git } = dependencies, { command, query } = repositoryRoutes(dependencies), changes = yield* acquireRepositoryChangePublisher(
|
|
7258
7979
|
git,
|
|
7259
7980
|
dependencies.watcher,
|
|
7260
7981
|
dependencies.events
|
|
7261
|
-
), readRefs = (repositoryId) =>
|
|
7982
|
+
), readRefs = (repositoryId) => Effect56.gen(function* () {
|
|
7262
7983
|
let repository = yield* access.repository(repositoryId);
|
|
7263
7984
|
return yield* changes.watch(repository), yield* readRepositoryRefs(git, repository);
|
|
7264
7985
|
}).pipe(
|
|
7265
|
-
|
|
7986
|
+
Effect56.mapError(
|
|
7266
7987
|
(error) => error._tag === "RepositoryRejected" ? error : repositoryRejected(
|
|
7267
7988
|
"GitFailed",
|
|
7268
7989
|
error._tag === "GitFailed" ? error.detail : "The repository catalog is unavailable."
|
|
@@ -7310,6 +8031,10 @@ function repositoryRefsFeature(dependencies) {
|
|
|
7310
8031
|
RepositoryTagsApi.delete,
|
|
7311
8032
|
tagPolicy,
|
|
7312
8033
|
(input, git2) => deleteTag(git2, input)
|
|
8034
|
+
),
|
|
8035
|
+
query(
|
|
8036
|
+
RepositoryTagsApi.annotation,
|
|
8037
|
+
(input, git2) => readTagAnnotation(git2, input)
|
|
7313
8038
|
)
|
|
7314
8039
|
]
|
|
7315
8040
|
};
|
|
@@ -7317,14 +8042,14 @@ function repositoryRefsFeature(dependencies) {
|
|
|
7317
8042
|
}
|
|
7318
8043
|
function createBranch(git, access, command) {
|
|
7319
8044
|
let { name, startPoint, track, worktreePath } = command;
|
|
7320
|
-
return
|
|
8045
|
+
return Effect56.gen(function* () {
|
|
7321
8046
|
yield* requireValidBranchName(git, worktreePath, name), track !== void 0 && (yield* requireRemoteBranch(git, worktreePath, track)), yield* runRepositoryGit(
|
|
7322
8047
|
git,
|
|
7323
8048
|
worktreePath,
|
|
7324
8049
|
["branch", "--no-track", name, startPoint],
|
|
7325
8050
|
refCommand
|
|
7326
8051
|
).pipe(
|
|
7327
|
-
|
|
8052
|
+
Effect56.mapError(
|
|
7328
8053
|
(error) => /not a valid object name/i.test(error.detail) ? { _tag: "RefMissing", name: startPoint } : branchWriteFailed(error, name)
|
|
7329
8054
|
)
|
|
7330
8055
|
), track !== void 0 && (yield* runRepositoryGit(
|
|
@@ -7332,20 +8057,20 @@ function createBranch(git, access, command) {
|
|
|
7332
8057
|
worktreePath,
|
|
7333
8058
|
setUpstreamArguments(name, track),
|
|
7334
8059
|
refCommand
|
|
7335
|
-
).pipe(
|
|
8060
|
+
).pipe(Effect56.mapError((error) => branchWriteFailed(error, name))));
|
|
7336
8061
|
let worktrees = yield* access.worktrees(worktreePath);
|
|
7337
8062
|
return yield* readLocalBranch(git, worktreePath, worktrees, name);
|
|
7338
8063
|
});
|
|
7339
8064
|
}
|
|
7340
8065
|
function setBranchUpstream(git, access, command) {
|
|
7341
8066
|
let { name, upstream, worktreePath } = command;
|
|
7342
|
-
return
|
|
8067
|
+
return Effect56.gen(function* () {
|
|
7343
8068
|
yield* requireBranchTarget(git, worktreePath, name, void 0), upstream === null ? yield* unsetUpstream(git, worktreePath, name) : (yield* requireRemoteBranch(git, worktreePath, upstream), yield* runRepositoryGit(
|
|
7344
8069
|
git,
|
|
7345
8070
|
worktreePath,
|
|
7346
8071
|
setUpstreamArguments(name, upstream),
|
|
7347
8072
|
refCommand
|
|
7348
|
-
).pipe(
|
|
8073
|
+
).pipe(Effect56.mapError((error) => branchWriteFailed(error, name))));
|
|
7349
8074
|
let worktrees = yield* access.worktrees(worktreePath);
|
|
7350
8075
|
return yield* readLocalBranch(git, worktreePath, worktrees, name);
|
|
7351
8076
|
});
|
|
@@ -7357,12 +8082,12 @@ function unsetUpstream(git, directory, name) {
|
|
|
7357
8082
|
["branch", "--unset-upstream", name],
|
|
7358
8083
|
refCommand
|
|
7359
8084
|
).pipe(
|
|
7360
|
-
|
|
7361
|
-
|
|
8085
|
+
Effect56.asVoid,
|
|
8086
|
+
Effect56.catchIf(
|
|
7362
8087
|
(error) => /has no upstream information/i.test(error.detail),
|
|
7363
|
-
() =>
|
|
8088
|
+
() => Effect56.void
|
|
7364
8089
|
),
|
|
7365
|
-
|
|
8090
|
+
Effect56.mapError((error) => branchWriteFailed(error, name))
|
|
7366
8091
|
);
|
|
7367
8092
|
}
|
|
7368
8093
|
|
|
@@ -7370,13 +8095,13 @@ function unsetUpstream(git, directory, name) {
|
|
|
7370
8095
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
7371
8096
|
import { eq as eq3 } from "drizzle-orm";
|
|
7372
8097
|
import { drizzle as drizzle2 } from "drizzle-orm/node-sqlite";
|
|
7373
|
-
import { Effect as
|
|
8098
|
+
import { Effect as Effect60, Semaphore as Semaphore3 } from "effect";
|
|
7374
8099
|
|
|
7375
8100
|
// src/apps/server/persistence/sqlite/database.ts
|
|
7376
8101
|
import { chmodSync } from "node:fs";
|
|
7377
8102
|
import { DatabaseSync } from "node:sqlite";
|
|
7378
8103
|
import { drizzle } from "drizzle-orm/node-sqlite";
|
|
7379
|
-
import { Effect as
|
|
8104
|
+
import { Effect as Effect57, Schedule } from "effect";
|
|
7380
8105
|
|
|
7381
8106
|
// src/apps/server/persistence/sqlite/migrations.ts
|
|
7382
8107
|
import { existsSync } from "node:fs";
|
|
@@ -7436,7 +8161,7 @@ function resolveMigrationsFolder() {
|
|
|
7436
8161
|
// src/apps/server/persistence/sqlite/database.ts
|
|
7437
8162
|
var busyTimeoutMilliseconds = 1e3, databaseLockRetryDelay = "10 millis", databaseLockRetryCount = 10;
|
|
7438
8163
|
function openEnvironmentDatabase(paths) {
|
|
7439
|
-
return
|
|
8164
|
+
return Effect57.gen(function* () {
|
|
7440
8165
|
let database = yield* storageSync(
|
|
7441
8166
|
"Could not open Environment state",
|
|
7442
8167
|
() => new DatabaseSync(paths.stateDatabase, {
|
|
@@ -7445,7 +8170,7 @@ function openEnvironmentDatabase(paths) {
|
|
|
7445
8170
|
timeout: busyTimeoutMilliseconds
|
|
7446
8171
|
})
|
|
7447
8172
|
);
|
|
7448
|
-
return yield*
|
|
8173
|
+
return yield* Effect57.gen(function* () {
|
|
7449
8174
|
return yield* storageSync(
|
|
7450
8175
|
"Could not open Environment state",
|
|
7451
8176
|
() => configureDatabase(database)
|
|
@@ -7453,11 +8178,11 @@ function openEnvironmentDatabase(paths) {
|
|
|
7453
8178
|
"Could not open Environment state",
|
|
7454
8179
|
() => restrictDatabasePermissions(paths.stateDatabase)
|
|
7455
8180
|
), database;
|
|
7456
|
-
}).pipe(
|
|
8181
|
+
}).pipe(Effect57.onError(() => closeEnvironmentDatabase(database)));
|
|
7457
8182
|
});
|
|
7458
8183
|
}
|
|
7459
8184
|
function closeEnvironmentDatabase(database) {
|
|
7460
|
-
return
|
|
8185
|
+
return Effect57.sync(() => database.close()).pipe(Effect57.orDie);
|
|
7461
8186
|
}
|
|
7462
8187
|
function configureDatabase(database) {
|
|
7463
8188
|
database.exec(`
|
|
@@ -7473,7 +8198,7 @@ function migrateDatabase(database) {
|
|
|
7473
8198
|
}
|
|
7474
8199
|
function retryDatabaseLock(effect) {
|
|
7475
8200
|
return effect.pipe(
|
|
7476
|
-
|
|
8201
|
+
Effect57.retry({
|
|
7477
8202
|
schedule: Schedule.spaced(databaseLockRetryDelay),
|
|
7478
8203
|
times: databaseLockRetryCount,
|
|
7479
8204
|
while: isDatabaseLocked
|
|
@@ -7499,19 +8224,19 @@ function restrictDatabasePermissions(path) {
|
|
|
7499
8224
|
|
|
7500
8225
|
// src/apps/server/persistence/storage/environment-paths.ts
|
|
7501
8226
|
import { chmod, mkdir as mkdir2 } from "node:fs/promises";
|
|
7502
|
-
import { join as
|
|
7503
|
-
import { Effect as
|
|
8227
|
+
import { join as join9 } from "node:path";
|
|
8228
|
+
import { Effect as Effect58 } from "effect";
|
|
7504
8229
|
function environmentPaths(root) {
|
|
7505
|
-
let cacheDirectory =
|
|
8230
|
+
let cacheDirectory = join9(root, "cache"), runtimeDirectory = join9(root, "runtime"), secretsDirectory = join9(root, "secrets"), settingsDirectory = join9(root, "settings"), stateDirectory = join9(root, "state");
|
|
7506
8231
|
return {
|
|
7507
8232
|
cacheDirectory,
|
|
7508
8233
|
root,
|
|
7509
8234
|
runtimeDirectory,
|
|
7510
|
-
runtimeMarker:
|
|
8235
|
+
runtimeMarker: join9(runtimeDirectory, "runtime.json"),
|
|
7511
8236
|
secretsDirectory,
|
|
7512
|
-
serverSecret:
|
|
8237
|
+
serverSecret: join9(secretsDirectory, "server.key"),
|
|
7513
8238
|
settingsDirectory,
|
|
7514
|
-
stateDatabase:
|
|
8239
|
+
stateDatabase: join9(stateDirectory, "state.sqlite"),
|
|
7515
8240
|
stateDirectory
|
|
7516
8241
|
};
|
|
7517
8242
|
}
|
|
@@ -7524,7 +8249,7 @@ function prepareEnvironmentDirectories(paths) {
|
|
|
7524
8249
|
paths.secretsDirectory,
|
|
7525
8250
|
paths.cacheDirectory
|
|
7526
8251
|
];
|
|
7527
|
-
return
|
|
8252
|
+
return Effect58.tryPromise({
|
|
7528
8253
|
try: async () => {
|
|
7529
8254
|
for (let directory of directories)
|
|
7530
8255
|
await mkdir2(directory, { mode: 448, recursive: !0 }), process.platform !== "win32" && await chmod(directory, 448);
|
|
@@ -7542,14 +8267,14 @@ import {
|
|
|
7542
8267
|
chmod as chmod2,
|
|
7543
8268
|
link,
|
|
7544
8269
|
mkdtemp as mkdtemp2,
|
|
7545
|
-
readFile as
|
|
8270
|
+
readFile as readFile5,
|
|
7546
8271
|
rm as rm4,
|
|
7547
8272
|
writeFile as writeFile4
|
|
7548
8273
|
} from "node:fs/promises";
|
|
7549
|
-
import { join as
|
|
7550
|
-
import { Effect as
|
|
8274
|
+
import { join as join10 } from "node:path";
|
|
8275
|
+
import { Effect as Effect59 } from "effect";
|
|
7551
8276
|
function ensureServerSecret(paths) {
|
|
7552
|
-
return
|
|
8277
|
+
return Effect59.tryPromise({
|
|
7553
8278
|
try: () => readOrCreateServerSecret(paths.serverSecret),
|
|
7554
8279
|
catch: (cause) => new EnvironmentStorageError({
|
|
7555
8280
|
cause,
|
|
@@ -7560,7 +8285,7 @@ function ensureServerSecret(paths) {
|
|
|
7560
8285
|
async function readOrCreateServerSecret(path) {
|
|
7561
8286
|
let secret;
|
|
7562
8287
|
try {
|
|
7563
|
-
secret = await
|
|
8288
|
+
secret = await readFile5(path, "utf8");
|
|
7564
8289
|
} catch (error) {
|
|
7565
8290
|
if (!isMissingFile2(error))
|
|
7566
8291
|
throw error;
|
|
@@ -7571,7 +8296,7 @@ async function readOrCreateServerSecret(path) {
|
|
|
7571
8296
|
return process.platform !== "win32" && await chmod2(path, 384), secret.trim();
|
|
7572
8297
|
}
|
|
7573
8298
|
async function publishServerSecret(path) {
|
|
7574
|
-
let directory = await mkdtemp2(`${path}.`), temporaryPath =
|
|
8299
|
+
let directory = await mkdtemp2(`${path}.`), temporaryPath = join10(directory, "key"), generated = `${randomBytes2(32).toString("base64url")}
|
|
7575
8300
|
`;
|
|
7576
8301
|
try {
|
|
7577
8302
|
await writeFile4(temporaryPath, generated, { flag: "wx", mode: 384 });
|
|
@@ -7579,7 +8304,7 @@ async function publishServerSecret(path) {
|
|
|
7579
8304
|
return await link(temporaryPath, path), generated;
|
|
7580
8305
|
} catch (error) {
|
|
7581
8306
|
if (!isExistingFile(error)) throw error;
|
|
7582
|
-
return await
|
|
8307
|
+
return await readFile5(path, "utf8");
|
|
7583
8308
|
}
|
|
7584
8309
|
} finally {
|
|
7585
8310
|
await rm4(directory, { recursive: !0, force: !0 });
|
|
@@ -7594,9 +8319,9 @@ function isExistingFile(error) {
|
|
|
7594
8319
|
|
|
7595
8320
|
// src/apps/server/persistence/environment-context.ts
|
|
7596
8321
|
function acquireEnvironmentContext(paths) {
|
|
7597
|
-
return
|
|
8322
|
+
return Effect60.gen(function* () {
|
|
7598
8323
|
yield* prepareEnvironmentDirectories(paths);
|
|
7599
|
-
let serverSecret = yield* ensureServerSecret(paths), database = yield*
|
|
8324
|
+
let serverSecret = yield* ensureServerSecret(paths), database = yield* Effect60.acquireRelease(
|
|
7600
8325
|
openEnvironmentDatabase(paths),
|
|
7601
8326
|
closeEnvironmentDatabase
|
|
7602
8327
|
), context = yield* storageSync(
|
|
@@ -7627,19 +8352,20 @@ function initializeEnvironment(context) {
|
|
|
7627
8352
|
|
|
7628
8353
|
// src/apps/server/repository/repository-coordination.ts
|
|
7629
8354
|
import { realpath as realpath5 } from "node:fs/promises";
|
|
7630
|
-
import { Effect as
|
|
8355
|
+
import { Effect as Effect62, Option as Option2, Semaphore as Semaphore4 } from "effect";
|
|
7631
8356
|
|
|
7632
8357
|
// src/apps/server/repository/repository-operation.ts
|
|
7633
8358
|
import { createHash as createHash4 } from "node:crypto";
|
|
7634
|
-
import { lstat as lstat3, readFile as
|
|
7635
|
-
import { join as
|
|
7636
|
-
import { Effect as
|
|
8359
|
+
import { lstat as lstat3, readFile as readFile6, stat as stat5 } from "node:fs/promises";
|
|
8360
|
+
import { join as join11 } from "node:path";
|
|
8361
|
+
import { Effect as Effect61 } from "effect";
|
|
7637
8362
|
var maximumMetadataBytes = 2 * 1024 * 1024, metadataNames = [
|
|
7638
8363
|
"HEAD",
|
|
7639
8364
|
"MERGE_HEAD",
|
|
7640
8365
|
"CHERRY_PICK_HEAD",
|
|
7641
8366
|
"REVERT_HEAD",
|
|
7642
8367
|
"MERGE_MSG",
|
|
8368
|
+
"SQUASH_MSG",
|
|
7643
8369
|
"AUTO_MERGE",
|
|
7644
8370
|
"rebase-merge/head-name",
|
|
7645
8371
|
"rebase-merge/onto",
|
|
@@ -7668,23 +8394,23 @@ var maximumMetadataBytes = 2 * 1024 * 1024, metadataNames = [
|
|
|
7668
8394
|
worktree: []
|
|
7669
8395
|
};
|
|
7670
8396
|
function readRepositoryOperation(git, worktreePath, directories) {
|
|
7671
|
-
return
|
|
8397
|
+
return Effect61.gen(function* () {
|
|
7672
8398
|
let { values, metadata, stamps } = yield* readOperationMetadata(
|
|
7673
8399
|
directories.gitDirectory
|
|
7674
|
-
), lock = yield* readGitLock(directories),
|
|
8400
|
+
), lock = yield* readGitLock(directories), candidate = identifyOperation(metadata, stamps), worktree = candidate === "idle" ? idleWorktree : yield* inspectWorktree(git, worktreePath, directories.gitDirectory), unresolvedPaths = [
|
|
7675
8401
|
...new Set(
|
|
7676
8402
|
worktree.unresolved.split("\0").filter(Boolean).map((line) => line.slice(line.indexOf(" ") + 1))
|
|
7677
8403
|
)
|
|
7678
|
-
].sort(), edit = kind === "rebase" && metadata["rebase-merge/amend"] !== null && unresolvedPaths.length === 0, reason = blockedReason(
|
|
8404
|
+
].sort(), kind = candidate === "squash" && unresolvedPaths.length === 0 ? "idle" : candidate, edit = kind === "rebase" && metadata["rebase-merge/amend"] !== null && unresolvedPaths.length === 0, empty = unresolvedPaths.length === 0 && (metadata.CHERRY_PICK_HEAD ?? metadata.REVERT_HEAD) !== null && !hasStagedChanges2(worktree.status), reason = empty ? lockedReason(lock) ?? "Nothing to commit. Skip this commit." : blockedReason(
|
|
7679
8405
|
kind,
|
|
7680
8406
|
lock,
|
|
7681
8407
|
unresolvedPaths.length,
|
|
7682
8408
|
edit,
|
|
7683
8409
|
worktree.status
|
|
7684
|
-
);
|
|
8410
|
+
), steps = kind === "rebase" ? rebaseSteps(metadata) : null, progress = stepProgress(steps) ?? operationProgress(metadata) ?? (yield* sequenceProgress(git, worktreePath, metadata, worktree.head));
|
|
7685
8411
|
return {
|
|
7686
8412
|
kind,
|
|
7687
|
-
phase: operationPhase(kind, unresolvedPaths.length, edit),
|
|
8413
|
+
phase: empty ? "empty" : operationPhase(kind, unresolvedPaths.length, edit),
|
|
7688
8414
|
actions: availableActions(kind, metadata, stamps, edit, lock, reason),
|
|
7689
8415
|
unresolvedPaths,
|
|
7690
8416
|
lock,
|
|
@@ -7693,21 +8419,22 @@ function readRepositoryOperation(git, worktreePath, directories) {
|
|
|
7693
8419
|
metadata["rebase-merge/head-name"] ?? metadata["rebase-apply/head-name"] ?? metadata.HEAD
|
|
7694
8420
|
),
|
|
7695
8421
|
commit: (kind === "merge" ? metadata.MERGE_HEAD?.split(`
|
|
7696
|
-
`)[0] : metadata["rebase-merge/stopped-sha"] ?? metadata["rebase-apply/original-commit"] ?? metadata.CHERRY_PICK_HEAD ?? metadata.REVERT_HEAD)?.trim() ?? null,
|
|
8422
|
+
`)[0] : kind === "squash" ? /^commit ([0-9a-f]+)$/m.exec(metadata.SQUASH_MSG ?? "")?.[1] : metadata["rebase-merge/stopped-sha"] ?? metadata["rebase-apply/original-commit"] ?? metadata.CHERRY_PICK_HEAD ?? metadata.REVERT_HEAD)?.trim() ?? null,
|
|
7697
8423
|
mergedBranch: kind === "merge" ? mergedBranchName(metadata.MERGE_MSG) : null,
|
|
7698
|
-
progress
|
|
8424
|
+
progress,
|
|
8425
|
+
steps
|
|
7699
8426
|
};
|
|
7700
8427
|
});
|
|
7701
8428
|
}
|
|
7702
8429
|
function readOperationMetadata(gitDirectory) {
|
|
7703
|
-
return
|
|
7704
|
-
let values = yield*
|
|
8430
|
+
return Effect61.gen(function* () {
|
|
8431
|
+
let values = yield* Effect61.forEach(
|
|
7705
8432
|
metadataNames,
|
|
7706
8433
|
(name) => readOperationFile(gitDirectory, name),
|
|
7707
8434
|
{ concurrency: 8 }
|
|
7708
8435
|
), metadata = Object.fromEntries(
|
|
7709
8436
|
metadataNames.map((name, index2) => [name, values[index2] ?? null])
|
|
7710
|
-
), stamps = yield*
|
|
8437
|
+
), stamps = yield* Effect61.forEach(
|
|
7711
8438
|
["rebase-merge", "rebase-apply", "sequencer"],
|
|
7712
8439
|
(name) => operationFileStamp(gitDirectory, name)
|
|
7713
8440
|
);
|
|
@@ -7715,7 +8442,7 @@ function readOperationMetadata(gitDirectory) {
|
|
|
7715
8442
|
});
|
|
7716
8443
|
}
|
|
7717
8444
|
function readGitLock({ gitDirectory, commonDirectory }) {
|
|
7718
|
-
return
|
|
8445
|
+
return Effect61.gen(function* () {
|
|
7719
8446
|
return (yield* operationFileStamp(gitDirectory, "index.lock")) !== null ? "index.lock" : (yield* operationFileStamp(commonDirectory, "packed-refs.lock")) !== null ? "packed-refs.lock" : null;
|
|
7720
8447
|
});
|
|
7721
8448
|
}
|
|
@@ -7723,13 +8450,13 @@ function operationPhase(kind, unresolved, edit) {
|
|
|
7723
8450
|
return kind === "idle" ? "idle" : kind === "unknown" ? "blocked" : unresolved ? "conflicts" : edit ? "edit" : "ready";
|
|
7724
8451
|
}
|
|
7725
8452
|
function inspectWorktree(git, worktreePath, gitDirectory) {
|
|
7726
|
-
return
|
|
8453
|
+
return Effect61.gen(function* () {
|
|
7727
8454
|
let head = yield* inspect(
|
|
7728
8455
|
git,
|
|
7729
8456
|
worktreePath,
|
|
7730
8457
|
["rev-parse", "--verify", "--quiet", "HEAD"],
|
|
7731
8458
|
[0, 1]
|
|
7732
|
-
).pipe(
|
|
8459
|
+
).pipe(Effect61.map((output) => output.trim() || null)), index2 = yield* operationFileStamp(gitDirectory, "index"), unresolved = yield* inspect(git, worktreePath, [
|
|
7733
8460
|
"ls-files",
|
|
7734
8461
|
"--unmerged",
|
|
7735
8462
|
"-z"
|
|
@@ -7739,7 +8466,7 @@ function inspectWorktree(git, worktreePath, gitDirectory) {
|
|
|
7739
8466
|
"--no-renames",
|
|
7740
8467
|
"-z",
|
|
7741
8468
|
"--untracked-files=no"
|
|
7742
|
-
]), worktree = yield*
|
|
8469
|
+
]), worktree = yield* Effect61.forEach(
|
|
7743
8470
|
status.split("\0").filter(Boolean),
|
|
7744
8471
|
(record) => operationFileStamp(worktreePath, record.slice(3)),
|
|
7745
8472
|
{ concurrency: 16 }
|
|
@@ -7753,7 +8480,7 @@ function inspect(git, worktreePath, args, exitCodes) {
|
|
|
7753
8480
|
worktreePath,
|
|
7754
8481
|
args,
|
|
7755
8482
|
exitCodes === void 0 ? {} : { exitCodes }
|
|
7756
|
-
).pipe(
|
|
8483
|
+
).pipe(Effect61.mapError((error) => inspectionFailed(error.detail)));
|
|
7757
8484
|
}
|
|
7758
8485
|
function identifyOperation(metadata, stamps) {
|
|
7759
8486
|
if (stamps[0] !== null) return "rebase";
|
|
@@ -7763,11 +8490,14 @@ function identifyOperation(metadata, stamps) {
|
|
|
7763
8490
|
if (metadata.CHERRY_PICK_HEAD !== null) return "cherry-pick";
|
|
7764
8491
|
if (metadata.REVERT_HEAD !== null) return "revert";
|
|
7765
8492
|
let todo = metadata["sequencer/todo"]?.trimStart();
|
|
7766
|
-
return todo?.startsWith("pick ") ? "cherry-pick" : todo?.startsWith("revert ") ? "revert" : stamps[2] === null ? "idle" : "
|
|
8493
|
+
return todo?.startsWith("pick ") ? "cherry-pick" : todo?.startsWith("revert ") ? "revert" : stamps[2] !== null ? "unknown" : metadata.SQUASH_MSG === null ? "idle" : "squash";
|
|
8494
|
+
}
|
|
8495
|
+
function lockedReason(lock) {
|
|
8496
|
+
return lock === null ? null : "Git is using this worktree. Check again when it finishes.";
|
|
7767
8497
|
}
|
|
7768
8498
|
function blockedReason(kind, lock, unresolved, edit, status) {
|
|
7769
|
-
|
|
7770
|
-
|
|
8499
|
+
let locked = lockedReason(lock);
|
|
8500
|
+
if (locked !== null) return locked;
|
|
7771
8501
|
if (unresolved)
|
|
7772
8502
|
return `Resolve and stage ${unresolved} ${unresolved === 1 ? "file" : "files"} to continue.`;
|
|
7773
8503
|
if (edit && status.length > 0)
|
|
@@ -7780,7 +8510,9 @@ function availableActions(kind, metadata, stamps, edit, lock, reason) {
|
|
|
7780
8510
|
let unlocked = {
|
|
7781
8511
|
enabled: lock === null,
|
|
7782
8512
|
reason: lock === null ? null : reason
|
|
7783
|
-
}
|
|
8513
|
+
};
|
|
8514
|
+
if (kind === "squash") return [{ action: "abort", ...unlocked }];
|
|
8515
|
+
let skippable = kind !== "merge" && !edit && !/no-commit\s*=\s*true/.test(metadata["sequencer/opts"] ?? "") && (kind !== "rebase" || metadata["rebase-merge/stopped-sha"] !== null || stamps[1] !== null);
|
|
7784
8516
|
return [
|
|
7785
8517
|
{ action: "continue", enabled: reason === null, reason },
|
|
7786
8518
|
...skippable ? [{ action: "skip", ...unlocked }] : [],
|
|
@@ -7793,6 +8525,45 @@ function branchName(headName) {
|
|
|
7793
8525
|
function mergedBranchName(message) {
|
|
7794
8526
|
return /^Merge (?:remote-tracking )?branch '([^']+)'/.exec(message ?? "")?.[1] ?? null;
|
|
7795
8527
|
}
|
|
8528
|
+
var planActions = {
|
|
8529
|
+
pick: "pick",
|
|
8530
|
+
p: "pick",
|
|
8531
|
+
reword: "reword",
|
|
8532
|
+
r: "reword",
|
|
8533
|
+
edit: "edit",
|
|
8534
|
+
e: "edit",
|
|
8535
|
+
squash: "squash",
|
|
8536
|
+
s: "squash",
|
|
8537
|
+
fixup: "fixup",
|
|
8538
|
+
f: "fixup",
|
|
8539
|
+
drop: "drop",
|
|
8540
|
+
d: "drop"
|
|
8541
|
+
};
|
|
8542
|
+
function rebaseSteps(metadata) {
|
|
8543
|
+
let steps = [];
|
|
8544
|
+
for (let [text3, done] of [
|
|
8545
|
+
[metadata["rebase-merge/done"], !0],
|
|
8546
|
+
[metadata["rebase-merge/git-rebase-todo"], !1]
|
|
8547
|
+
])
|
|
8548
|
+
for (let line of (text3 ?? "").split(`
|
|
8549
|
+
`)) {
|
|
8550
|
+
let trimmed = line.trim();
|
|
8551
|
+
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
8552
|
+
let match = /^(\w+)\s+([0-9a-f]{40,64})(?:\s+(.*))?$/.exec(trimmed), action = match?.[1] === void 0 ? void 0 : planActions[match[1]];
|
|
8553
|
+
if (match === null || action === void 0) return null;
|
|
8554
|
+
steps.push({
|
|
8555
|
+
commit: match[2] ?? "",
|
|
8556
|
+
action,
|
|
8557
|
+
subject: (match[3] ?? "").replace(/^#\s*/, ""),
|
|
8558
|
+
done
|
|
8559
|
+
});
|
|
8560
|
+
}
|
|
8561
|
+
return steps.length === 0 ? null : steps;
|
|
8562
|
+
}
|
|
8563
|
+
function stepProgress(steps) {
|
|
8564
|
+
let current = steps?.filter((step) => step.done).length ?? 0;
|
|
8565
|
+
return steps === null || current === 0 ? null : { current, total: steps.length };
|
|
8566
|
+
}
|
|
7796
8567
|
function operationProgress(metadata) {
|
|
7797
8568
|
let current = Number(
|
|
7798
8569
|
metadata["rebase-merge/msgnum"] ?? metadata["rebase-apply/next"]
|
|
@@ -7801,14 +8572,31 @@ function operationProgress(metadata) {
|
|
|
7801
8572
|
);
|
|
7802
8573
|
return Number.isSafeInteger(current) && Number.isSafeInteger(total) && current > 0 && total >= current ? { current, total } : null;
|
|
7803
8574
|
}
|
|
8575
|
+
function hasStagedChanges2(status) {
|
|
8576
|
+
return status.split("\0").some((line) => line.length > 2 && line[0] !== " " && line[0] !== "?");
|
|
8577
|
+
}
|
|
8578
|
+
function sequenceProgress(git, worktreePath, metadata, head) {
|
|
8579
|
+
let start = metadata["sequencer/head"]?.trim(), remaining = (metadata["sequencer/todo"] ?? "").split(`
|
|
8580
|
+
`).filter((line) => /^(pick|revert|p|r)\s/.test(line)).length;
|
|
8581
|
+
return start === void 0 || head === null || remaining === 0 || /no-commit\s*=\s*true/.test(metadata["sequencer/opts"] ?? "") ? Effect61.succeed(null) : inspect(git, worktreePath, [
|
|
8582
|
+
"rev-list",
|
|
8583
|
+
"--count",
|
|
8584
|
+
`${start}..${head}`
|
|
8585
|
+
]).pipe(
|
|
8586
|
+
Effect61.map((output) => {
|
|
8587
|
+
let done = Number(output.trim());
|
|
8588
|
+
return Number.isSafeInteger(done) ? { current: done + 1, total: done + remaining } : null;
|
|
8589
|
+
})
|
|
8590
|
+
);
|
|
8591
|
+
}
|
|
7804
8592
|
function readOperationFile(directory, name) {
|
|
7805
|
-
return
|
|
8593
|
+
return Effect61.tryPromise({
|
|
7806
8594
|
try: async () => {
|
|
7807
8595
|
try {
|
|
7808
|
-
let path =
|
|
8596
|
+
let path = join11(directory, name);
|
|
7809
8597
|
if ((await stat5(path)).size > maximumMetadataBytes)
|
|
7810
8598
|
throw new Error("Operation metadata is too large.");
|
|
7811
|
-
return await
|
|
8599
|
+
return await readFile6(path, "utf8");
|
|
7812
8600
|
} catch (error) {
|
|
7813
8601
|
if (isMissing(error)) return null;
|
|
7814
8602
|
throw error;
|
|
@@ -7818,10 +8606,10 @@ function readOperationFile(directory, name) {
|
|
|
7818
8606
|
});
|
|
7819
8607
|
}
|
|
7820
8608
|
function operationFileStamp(directory, name) {
|
|
7821
|
-
return
|
|
8609
|
+
return Effect61.tryPromise({
|
|
7822
8610
|
try: async () => {
|
|
7823
8611
|
try {
|
|
7824
|
-
let info = await lstat3(
|
|
8612
|
+
let info = await lstat3(join11(directory, name), { bigint: !0 });
|
|
7825
8613
|
return `${info.ino}:${info.size}:${info.mtimeNs}:${info.ctimeNs}`;
|
|
7826
8614
|
} catch (error) {
|
|
7827
8615
|
if (isMissing(error)) return null;
|
|
@@ -7840,16 +8628,16 @@ function isMissing(error) {
|
|
|
7840
8628
|
|
|
7841
8629
|
// src/apps/server/repository/repository-coordination.ts
|
|
7842
8630
|
function createRepositoryCoordination(git) {
|
|
7843
|
-
let locks = /* @__PURE__ */ new Map(), withLock = (key, acquisition, operation) =>
|
|
7844
|
-
|
|
8631
|
+
let locks = /* @__PURE__ */ new Map(), withLock = (key, acquisition, operation) => Effect62.acquireUseRelease(
|
|
8632
|
+
Effect62.sync(() => {
|
|
7845
8633
|
let entry = locks.get(key);
|
|
7846
8634
|
return entry === void 0 && (entry = { semaphore: Semaphore4.makeUnsafe(1), owners: 0 }, locks.set(key, entry)), entry.owners++, entry;
|
|
7847
8635
|
}),
|
|
7848
8636
|
(entry) => acquisition === "wait" ? entry.semaphore.withPermit(operation) : entry.semaphore.withPermitsIfAvailable(1)(operation).pipe(
|
|
7849
|
-
|
|
8637
|
+
Effect62.flatMap(
|
|
7850
8638
|
Option2.match({
|
|
7851
|
-
onSome:
|
|
7852
|
-
onNone: () =>
|
|
8639
|
+
onSome: Effect62.succeed,
|
|
8640
|
+
onNone: () => Effect62.fail(
|
|
7853
8641
|
repositoryRejected(
|
|
7854
8642
|
"Busy",
|
|
7855
8643
|
"Another repository write is in progress."
|
|
@@ -7858,35 +8646,35 @@ function createRepositoryCoordination(git) {
|
|
|
7858
8646
|
})
|
|
7859
8647
|
)
|
|
7860
8648
|
),
|
|
7861
|
-
(entry) =>
|
|
8649
|
+
(entry) => Effect62.sync(() => {
|
|
7862
8650
|
--entry.owners === 0 && locks.delete(key);
|
|
7863
8651
|
})
|
|
7864
|
-
), directories = /* @__PURE__ */ new Map(), gitDirectories = (directory) =>
|
|
8652
|
+
), directories = /* @__PURE__ */ new Map(), gitDirectories = (directory) => Effect62.gen(function* () {
|
|
7865
8653
|
let identity = yield* readGitEntryIdentity(directory), cached = directories.get(directory);
|
|
7866
8654
|
if (identity !== void 0 && cached?.identity === identity)
|
|
7867
8655
|
return cached.paths;
|
|
7868
8656
|
let paths = yield* resolveGitDirectories(git, directory);
|
|
7869
8657
|
return identity !== void 0 && directories.set(directory, { identity, paths }), paths;
|
|
7870
8658
|
}), forgetOnError = (directory, effect) => effect.pipe(
|
|
7871
|
-
|
|
8659
|
+
Effect62.onError(() => Effect62.sync(() => directories.delete(directory)))
|
|
7872
8660
|
);
|
|
7873
8661
|
return {
|
|
7874
8662
|
run: (directory, policy, operation) => forgetOnError(
|
|
7875
8663
|
directory,
|
|
7876
|
-
|
|
8664
|
+
Effect62.gen(function* () {
|
|
7877
8665
|
let { refs, worktree } = policy.locks, paths = yield* gitDirectories(directory), guarded = requireCompatibleWrite(
|
|
7878
8666
|
git,
|
|
7879
8667
|
directory,
|
|
7880
8668
|
paths,
|
|
7881
8669
|
policy
|
|
7882
|
-
).pipe(
|
|
8670
|
+
).pipe(Effect62.andThen(operation)), inWorktree = worktree === void 0 ? guarded : withLock(`worktree:${paths.gitDirectory}`, worktree, guarded);
|
|
7883
8671
|
return yield* refs === void 0 ? inWorktree : withLock(`refs:${paths.commonDirectory}`, refs, inWorktree);
|
|
7884
8672
|
})
|
|
7885
8673
|
),
|
|
7886
8674
|
operation: (directory) => forgetOnError(
|
|
7887
8675
|
directory,
|
|
7888
8676
|
gitDirectories(directory).pipe(
|
|
7889
|
-
|
|
8677
|
+
Effect62.flatMap(
|
|
7890
8678
|
(paths) => readRepositoryOperation(git, directory, paths)
|
|
7891
8679
|
)
|
|
7892
8680
|
)
|
|
@@ -7894,10 +8682,10 @@ function createRepositoryCoordination(git) {
|
|
|
7894
8682
|
};
|
|
7895
8683
|
}
|
|
7896
8684
|
function requireCompatibleWrite(git, directory, paths, { name, duringOperation }) {
|
|
7897
|
-
return duringOperation === "proceed" ?
|
|
7898
|
-
|
|
8685
|
+
return duringOperation === "proceed" ? Effect62.void : readRepositoryOperation(git, directory, paths).pipe(
|
|
8686
|
+
Effect62.flatMap((state) => state.lock !== null ? Effect62.fail(
|
|
7899
8687
|
repositoryRejected("Busy", `Git lock exists: ${state.lock}.`)
|
|
7900
|
-
) : state.kind === "idle" || duringOperation !== "block" && duringOperation.allowWhen(state) ?
|
|
8688
|
+
) : state.kind === "idle" || duringOperation !== "block" && duringOperation.allowWhen(state) ? Effect62.void : Effect62.fail(
|
|
7901
8689
|
repositoryRejected(
|
|
7902
8690
|
"Incompatible",
|
|
7903
8691
|
`Cannot ${name} while ${state.kind === "unknown" ? "an unrecognized Git operation is" : `${state.kind} is`} in progress.`
|
|
@@ -7906,12 +8694,12 @@ function requireCompatibleWrite(git, directory, paths, { name, duringOperation }
|
|
|
7906
8694
|
);
|
|
7907
8695
|
}
|
|
7908
8696
|
function resolveGitDirectories(git, directory) {
|
|
7909
|
-
return
|
|
8697
|
+
return Effect62.gen(function* () {
|
|
7910
8698
|
let gitDirectory = (yield* runRepositoryGit(git, directory, [
|
|
7911
8699
|
"rev-parse",
|
|
7912
8700
|
"--absolute-git-dir"
|
|
7913
8701
|
])).trimEnd(), commonDirectory = yield* readGitCommonDirectory(git, directory);
|
|
7914
|
-
return yield*
|
|
8702
|
+
return yield* Effect62.tryPromise({
|
|
7915
8703
|
try: async () => ({
|
|
7916
8704
|
gitDirectory: await realpath5(gitDirectory),
|
|
7917
8705
|
commonDirectory: await realpath5(commonDirectory)
|
|
@@ -7922,7 +8710,7 @@ function resolveGitDirectories(git, directory) {
|
|
|
7922
8710
|
)
|
|
7923
8711
|
});
|
|
7924
8712
|
}).pipe(
|
|
7925
|
-
|
|
8713
|
+
Effect62.mapError(
|
|
7926
8714
|
(error) => error._tag === "GitFailed" ? repositoryRejected("GitFailed", error.detail) : error
|
|
7927
8715
|
)
|
|
7928
8716
|
);
|
|
@@ -7930,7 +8718,7 @@ function resolveGitDirectories(git, directory) {
|
|
|
7930
8718
|
|
|
7931
8719
|
// src/apps/server/app/server/serve-environment.ts
|
|
7932
8720
|
function serveEnvironment(dependencies, options) {
|
|
7933
|
-
return
|
|
8721
|
+
return Effect63.gen(function* () {
|
|
7934
8722
|
let environment = yield* readCurrentEnvironment(dependencies.context), useAutomaticPort = options.port === void 0 || options.port === 0, listener = yield* acquireEnvironmentListener({
|
|
7935
8723
|
authorization: dependencies.authorization,
|
|
7936
8724
|
...options.browserAssetsRoot === void 0 ? {} : { browserAssetsRoot: options.browserAssetsRoot },
|
|
@@ -7956,8 +8744,8 @@ function serveEnvironment(dependencies, options) {
|
|
|
7956
8744
|
});
|
|
7957
8745
|
}
|
|
7958
8746
|
function acquireEnvironment(home, git) {
|
|
7959
|
-
return
|
|
7960
|
-
let paths = environmentPaths(
|
|
8747
|
+
return Effect63.gen(function* () {
|
|
8748
|
+
let paths = environmentPaths(join12(home, ".rebase")), context = yield* acquireEnvironmentContext(paths), watcher = createLocalRepositoryWatcher(), catalog = createRepositoryCatalog(context, git);
|
|
7961
8749
|
return {
|
|
7962
8750
|
access: createRepositoryAccess(catalog, git, watcher),
|
|
7963
8751
|
authorization: createEnvironmentAuthorization(context),
|
|
@@ -7972,7 +8760,7 @@ function acquireEnvironment(home, git) {
|
|
|
7972
8760
|
});
|
|
7973
8761
|
}
|
|
7974
8762
|
function environmentFeatures(dependencies) {
|
|
7975
|
-
return
|
|
8763
|
+
return Effect63.gen(function* () {
|
|
7976
8764
|
return combineEnvironmentFeatures([
|
|
7977
8765
|
environmentAuthorizationFeature(dependencies.authorization),
|
|
7978
8766
|
environmentFilesystemFeature(),
|
|
@@ -8030,7 +8818,7 @@ function hasNoAutomaticPort() {
|
|
|
8030
8818
|
|
|
8031
8819
|
// src/apps/server/app/server/start-environment-server.ts
|
|
8032
8820
|
function startEnvironmentServer(options) {
|
|
8033
|
-
return
|
|
8821
|
+
return Effect64.gen(function* () {
|
|
8034
8822
|
return yield* verifyRuntimeRequirements, yield* serveEnvironment(
|
|
8035
8823
|
yield* acquireEnvironment(options.home, createLocalGitCommandRunner()),
|
|
8036
8824
|
options
|
|
@@ -8042,20 +8830,20 @@ function startEnvironmentServer(options) {
|
|
|
8042
8830
|
var usage = "Usage: rebase [serve] [--host <ip-address|lan|tailscale>] [--port <1-65535>]";
|
|
8043
8831
|
function runCli(arguments_ = process.argv.slice(2)) {
|
|
8044
8832
|
if (arguments_.includes("--help") || arguments_.includes("-h"))
|
|
8045
|
-
return writeOutput(usage).pipe(
|
|
8833
|
+
return writeOutput(usage).pipe(Effect65.as(0));
|
|
8046
8834
|
if (arguments_.includes("--version") || arguments_.includes("-v"))
|
|
8047
|
-
return writeOutput(versionOutput()).pipe(
|
|
8048
|
-
let program =
|
|
8835
|
+
return writeOutput(versionOutput()).pipe(Effect65.as(0));
|
|
8836
|
+
let program = Effect65.try({
|
|
8049
8837
|
try: () => parseServeArguments(arguments_),
|
|
8050
8838
|
catch: normalizeError
|
|
8051
|
-
}).pipe(
|
|
8052
|
-
return
|
|
8053
|
-
onFailure: (error) => writeFailure(`rebase serve failed: ${error.message}`).pipe(
|
|
8054
|
-
onSuccess: () =>
|
|
8839
|
+
}).pipe(Effect65.flatMap(serve));
|
|
8840
|
+
return Effect65.matchEffect(program, {
|
|
8841
|
+
onFailure: (error) => writeFailure(`rebase serve failed: ${error.message}`).pipe(Effect65.as(1)),
|
|
8842
|
+
onSuccess: () => Effect65.succeed(0)
|
|
8055
8843
|
});
|
|
8056
8844
|
}
|
|
8057
8845
|
async function main() {
|
|
8058
|
-
process.exitCode = await
|
|
8846
|
+
process.exitCode = await Effect65.runPromise(runCli());
|
|
8059
8847
|
}
|
|
8060
8848
|
function parseServeArguments(arguments_) {
|
|
8061
8849
|
let normalizedArguments = arguments_.length === 0 || arguments_[0]?.startsWith("--") ? ["serve", ...arguments_] : arguments_, [command, ...options] = normalizedArguments;
|
|
@@ -8083,13 +8871,13 @@ function parsePort(value) {
|
|
|
8083
8871
|
return port;
|
|
8084
8872
|
}
|
|
8085
8873
|
function serve(options) {
|
|
8086
|
-
return
|
|
8087
|
-
|
|
8088
|
-
let shutdown = yield* acquireShutdownSignal(), server = yield*
|
|
8874
|
+
return Effect65.scoped(
|
|
8875
|
+
Effect65.gen(function* () {
|
|
8876
|
+
let shutdown = yield* acquireShutdownSignal(), server = yield* Effect65.raceFirst(
|
|
8089
8877
|
startEnvironmentServer(options),
|
|
8090
8878
|
Deferred3.await(shutdown)
|
|
8091
8879
|
);
|
|
8092
|
-
server !== void 0 && (yield*
|
|
8880
|
+
server !== void 0 && (yield* Effect65.sync(() => {
|
|
8093
8881
|
process.stdout.write(`Listening URL: ${server.origin}
|
|
8094
8882
|
`), process.stdout.write(`Pairing URL: ${server.pairingUrl}
|
|
8095
8883
|
`), openDefaultBrowser(server.pairingUrl);
|
|
@@ -8098,29 +8886,29 @@ function serve(options) {
|
|
|
8098
8886
|
);
|
|
8099
8887
|
}
|
|
8100
8888
|
function acquireShutdownSignal() {
|
|
8101
|
-
return
|
|
8889
|
+
return Effect65.gen(function* () {
|
|
8102
8890
|
let shutdown = yield* Deferred3.make();
|
|
8103
|
-
return yield*
|
|
8104
|
-
|
|
8891
|
+
return yield* Effect65.acquireRelease(
|
|
8892
|
+
Effect65.sync(() => {
|
|
8105
8893
|
let stop = () => {
|
|
8106
|
-
Deferred3.doneUnsafe(shutdown,
|
|
8894
|
+
Deferred3.doneUnsafe(shutdown, Effect65.void);
|
|
8107
8895
|
};
|
|
8108
8896
|
return process.once("SIGINT", stop), process.once("SIGTERM", stop), stop;
|
|
8109
8897
|
}),
|
|
8110
|
-
(stop) =>
|
|
8898
|
+
(stop) => Effect65.sync(() => {
|
|
8111
8899
|
process.off("SIGINT", stop), process.off("SIGTERM", stop);
|
|
8112
8900
|
})
|
|
8113
8901
|
), shutdown;
|
|
8114
8902
|
});
|
|
8115
8903
|
}
|
|
8116
8904
|
function writeOutput(line) {
|
|
8117
|
-
return
|
|
8905
|
+
return Effect65.sync(() => {
|
|
8118
8906
|
process.stdout.write(`${line}
|
|
8119
8907
|
`);
|
|
8120
8908
|
});
|
|
8121
8909
|
}
|
|
8122
8910
|
function writeFailure(line) {
|
|
8123
|
-
return
|
|
8911
|
+
return Effect65.sync(() => {
|
|
8124
8912
|
process.stderr.write(`${line}
|
|
8125
8913
|
`);
|
|
8126
8914
|
});
|
|
@@ -8140,7 +8928,7 @@ function resolveBrowserAssetsRoot() {
|
|
|
8140
8928
|
return fileURLToPath2(root);
|
|
8141
8929
|
}
|
|
8142
8930
|
function productVersion() {
|
|
8143
|
-
return "0.0.2
|
|
8931
|
+
return "0.0.2";
|
|
8144
8932
|
}
|
|
8145
8933
|
function versionOutput() {
|
|
8146
8934
|
return [
|