Skip to content

Commit 3aa2c6e

Browse files
Merge pull request #12 from alvarosanchez/followup/copilot-pr11-feedback
Address Copilot follow-up feedback from PR #11
2 parents 9a299d7 + 0b48921 commit 3aa2c6e

File tree

2 files changed

+170
-6
lines changed

2 files changed

+170
-6
lines changed

src/main/java/com/github/alvarosanchez/ocp/command/RepositoryRefreshCommand.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,37 +45,60 @@ public Integer call() {
4545
}
4646

4747
private RefreshOutcome refreshSingleWithConflictResolution(String name) {
48-
ProfileService.RefreshConflictResolution appliedResolution = null;
48+
boolean discardedRepositoryLocalChanges = false;
49+
boolean forcePushedLocalChanges = false;
50+
boolean reappliedMergedProfileFiles = false;
4951
while (true) {
5052
try {
5153
ProfileService.ProfileRefreshResult refreshResult = profileService.refreshRepositoryWithDetails(name);
52-
if (appliedResolution == ProfileService.RefreshConflictResolution.DISCARD_AND_REFRESH) {
54+
if (forcePushedLocalChanges && reappliedMergedProfileFiles) {
5355
return new RefreshOutcome(
54-
"Discarded local changes and refreshed repository `" + name + "`.",
56+
"Reapplied active profile, committed local changes, force-pushed, and refreshed repository `" + name + "`.",
5557
refreshResult
5658
);
5759
}
58-
if (appliedResolution == ProfileService.RefreshConflictResolution.COMMIT_AND_FORCE_PUSH) {
60+
if (forcePushedLocalChanges) {
5961
return new RefreshOutcome(
6062
"Committed local changes, force-pushed, and refreshed repository `" + name + "`.",
6163
refreshResult
6264
);
6365
}
66+
if (discardedRepositoryLocalChanges && reappliedMergedProfileFiles) {
67+
return new RefreshOutcome(
68+
"Reapplied active profile, discarded repository local changes, and refreshed repository `" + name + "`.",
69+
refreshResult
70+
);
71+
}
72+
if (discardedRepositoryLocalChanges) {
73+
return new RefreshOutcome(
74+
"Discarded local changes and refreshed repository `" + name + "`.",
75+
refreshResult
76+
);
77+
}
78+
if (reappliedMergedProfileFiles) {
79+
return new RefreshOutcome(
80+
"Reapplied active profile and refreshed repository `" + name + "`.",
81+
refreshResult
82+
);
83+
}
6484
return new RefreshOutcome("Refreshed repository `" + name + "`.", refreshResult);
6585
} catch (ProfileService.ProfileRefreshConflictException conflict) {
6686
ProfileService.RefreshConflictResolution resolution = promptRefreshConflictResolution(conflict);
6787
if (resolution == ProfileService.RefreshConflictResolution.DO_NOTHING) {
6888
throw new IllegalStateException("Refresh cancelled. Local changes were left untouched.");
6989
}
7090
applyRefreshConflictResolution(conflict, resolution);
71-
appliedResolution = resolution;
91+
discardedRepositoryLocalChanges = discardedRepositoryLocalChanges
92+
|| resolution == ProfileService.RefreshConflictResolution.DISCARD_AND_REFRESH;
93+
forcePushedLocalChanges = forcePushedLocalChanges
94+
|| resolution == ProfileService.RefreshConflictResolution.COMMIT_AND_FORCE_PUSH;
7295
} catch (ProfileService.ProfileRefreshUserConfigConflictException conflict) {
7396
ProfileService.RefreshConflictResolution resolution = promptMergedProfileRefreshConflictResolution(conflict);
7497
if (resolution == ProfileService.RefreshConflictResolution.DO_NOTHING) {
7598
throw new IllegalStateException("Refresh cancelled. Local changes were left untouched.");
7699
}
77100
applyMergedProfileRefreshConflictResolution(conflict, resolution);
78-
appliedResolution = resolution;
101+
reappliedMergedProfileFiles = true;
79102
}
80103
}
81104
}

src/test/java/com/github/alvarosanchez/ocp/command/ProfileCommandTest.java

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,147 @@ void repositoryRefreshAllPromptsWhenMergedActiveProfileFilesHaveLocalChangesAndC
715715
assertEquals("edit", unchanged.get("local"));
716716
}
717717

718+
@Test
719+
void repositoryRefreshSinglePromptsWhenMergedActiveProfileFilesHaveLocalChangesAndCanDiscardThenRefresh() throws IOException, InterruptedException {
720+
RemoteRepositoryState state = createRemoteInheritedProfileRepository("oca", "oca-personal");
721+
722+
writeOcpConfig(
723+
new OcpConfigFile(
724+
new OcpConfigOptions(),
725+
List.of(new RepositoryEntry("repo-refresh-inherited", state.remoteUri(), null))
726+
)
727+
);
728+
729+
runCommand(List.of("git", "clone", state.remoteUri(), state.localClone().toString()));
730+
731+
CommandResult useResult = execute("profile", "use", "oca-personal");
732+
assertEquals(0, useResult.exitCode());
733+
734+
Path opencodeFile = Path.of(System.getProperty("ocp.opencode.config.dir")).resolve("opencode.json");
735+
Files.writeString(opencodeFile, "{\"some_parent\":\"local\",\"shared\":\"child\",\"local\":\"edit\"}");
736+
737+
CommandResult refreshResult = executeWithInput("1\n", "repository", "refresh", "repo-refresh-inherited");
738+
739+
assertEquals(0, refreshResult.exitCode());
740+
assertTrue(refreshResult.stdout().contains("Local changes detected in merged active profile files for profile `oca-personal`"));
741+
assertTrue(refreshResult.stdout().contains("Discarding local changes in merged user config files and retrying refresh"));
742+
assertTrue(refreshResult.stdout().contains("Reapplied active profile and refreshed repository `repo-refresh-inherited`."));
743+
Map<String, Object> refreshed = readJsonMap(opencodeFile);
744+
assertEquals("v1", refreshed.get("some_parent"));
745+
assertEquals("child", refreshed.get("shared"));
746+
assertFalse(refreshed.containsKey("local"));
747+
}
748+
749+
@Test
750+
void repositoryRefreshSinglePromptsWhenMergedActiveProfileFilesHaveLocalChangesAndCanAbortWithoutChanges() throws IOException, InterruptedException {
751+
RemoteRepositoryState state = createRemoteInheritedProfileRepository("oca", "oca-personal");
752+
753+
writeOcpConfig(
754+
new OcpConfigFile(
755+
new OcpConfigOptions(),
756+
List.of(new RepositoryEntry("repo-refresh-inherited", state.remoteUri(), null))
757+
)
758+
);
759+
760+
runCommand(List.of("git", "clone", state.remoteUri(), state.localClone().toString()));
761+
762+
CommandResult useResult = execute("profile", "use", "oca-personal");
763+
assertEquals(0, useResult.exitCode());
764+
765+
Path opencodeFile = Path.of(System.getProperty("ocp.opencode.config.dir")).resolve("opencode.json");
766+
Files.writeString(opencodeFile, "{\"some_parent\":\"local\",\"shared\":\"child\",\"local\":\"edit\"}");
767+
768+
CommandResult refreshResult = executeWithInput("2\n", "repository", "refresh", "repo-refresh-inherited");
769+
770+
assertEquals(1, refreshResult.exitCode());
771+
assertTrue(refreshResult.stdout().contains("Local changes detected in merged active profile files for profile `oca-personal`"));
772+
assertTrue(refreshResult.stderr().contains("Refresh cancelled"));
773+
Map<String, Object> unchanged = readJsonMap(opencodeFile);
774+
assertEquals("local", unchanged.get("some_parent"));
775+
assertEquals("child", unchanged.get("shared"));
776+
assertEquals("edit", unchanged.get("local"));
777+
}
778+
779+
@Test
780+
void repositoryRefreshSingleHandlesMergedAndRepositoryConflictsWhenBothDiscarded() throws IOException, InterruptedException {
781+
RemoteRepositoryState state = createRemoteInheritedProfileRepository("oca", "oca-personal");
782+
783+
writeOcpConfig(
784+
new OcpConfigFile(
785+
new OcpConfigOptions(),
786+
List.of(new RepositoryEntry("repo-refresh-inherited", state.remoteUri(), null))
787+
)
788+
);
789+
790+
runCommand(List.of("git", "clone", state.remoteUri(), state.localClone().toString()));
791+
792+
CommandResult useResult = execute("profile", "use", "oca-personal");
793+
assertEquals(0, useResult.exitCode());
794+
795+
Path opencodeFile = Path.of(System.getProperty("ocp.opencode.config.dir")).resolve("opencode.json");
796+
Files.writeString(opencodeFile, "{\"some_parent\":\"drift\",\"shared\":\"child\",\"local\":\"edit\"}");
797+
798+
Path repositoryScratchFile = state.localClone().resolve("local-change.txt");
799+
Files.writeString(repositoryScratchFile, "local-repository-change");
800+
801+
CommandResult refreshResult = executeWithInput("1\n1\n", "repository", "refresh", "repo-refresh-inherited");
802+
803+
assertEquals(0, refreshResult.exitCode());
804+
assertTrue(refreshResult.stdout().contains("Local changes detected in merged active profile files for profile `oca-personal`"));
805+
assertTrue(refreshResult.stdout().contains("Local uncommitted changes detected in repository `repo-refresh-inherited`"));
806+
assertTrue(
807+
refreshResult
808+
.stdout()
809+
.contains("Reapplied active profile, discarded repository local changes, and refreshed repository `repo-refresh-inherited`.")
810+
);
811+
812+
Map<String, Object> refreshed = readJsonMap(opencodeFile);
813+
assertEquals("v1", refreshed.get("some_parent"));
814+
assertEquals("child", refreshed.get("shared"));
815+
assertFalse(refreshed.containsKey("local"));
816+
assertTrue(Files.notExists(repositoryScratchFile));
817+
}
818+
819+
@Test
820+
void repositoryRefreshSingleHandlesMergedAndRepositoryConflictsWhenRepositoryIsCommitted() throws IOException, InterruptedException {
821+
RemoteRepositoryState state = createRemoteInheritedProfileRepository("oca", "oca-personal");
822+
823+
writeOcpConfig(
824+
new OcpConfigFile(
825+
new OcpConfigOptions(),
826+
List.of(new RepositoryEntry("repo-refresh-inherited", state.remoteUri(), null))
827+
)
828+
);
829+
830+
runCommand(List.of("git", "clone", state.remoteUri(), state.localClone().toString()));
831+
832+
CommandResult useResult = execute("profile", "use", "oca-personal");
833+
assertEquals(0, useResult.exitCode());
834+
835+
Path opencodeFile = Path.of(System.getProperty("ocp.opencode.config.dir")).resolve("opencode.json");
836+
Files.writeString(opencodeFile, "{\"some_parent\":\"drift\",\"shared\":\"child\",\"local\":\"edit\"}");
837+
838+
Path repositoryScratchFile = state.localClone().resolve("local-change.txt");
839+
Files.writeString(repositoryScratchFile, "local-repository-change");
840+
841+
CommandResult refreshResult = executeWithInput("1\n2\n", "repository", "refresh", "repo-refresh-inherited");
842+
843+
assertEquals(0, refreshResult.exitCode());
844+
assertTrue(refreshResult.stdout().contains("Local changes detected in merged active profile files for profile `oca-personal`"));
845+
assertTrue(refreshResult.stdout().contains("Local uncommitted changes detected in repository `repo-refresh-inherited`"));
846+
assertTrue(
847+
refreshResult
848+
.stdout()
849+
.contains("Reapplied active profile, committed local changes, force-pushed, and refreshed repository `repo-refresh-inherited`.")
850+
);
851+
852+
Map<String, Object> refreshed = readJsonMap(opencodeFile);
853+
assertEquals("v1", refreshed.get("some_parent"));
854+
assertEquals("child", refreshed.get("shared"));
855+
assertFalse(refreshed.containsKey("local"));
856+
assertEquals("local-repository-change", Files.readString(repositoryScratchFile));
857+
}
858+
718859
@Test
719860
void repositoryRefreshPromptsWhenRepositoryHasLocalChangesAndCanDiscardThenRefresh() throws IOException, InterruptedException {
720861
RemoteRepositoryState state = createRemoteProfileRepository("ops");

0 commit comments

Comments
 (0)