Conversation
dara-abijo-adfa
commented
Apr 8, 2026
- Detect merge conflicts
- Resolve conflicted files in the editor
📝 WalkthroughRelease NotesFeatures
UI Updates
Technical Changes
Risks and Best-Practices Violations
WalkthroughAdds merge and abort-merge support across git core, viewmodel, and UI; introduces PullUiState.Conflicts, an Abort Merge button/flow, and exposes EditorHandlerActivity.checkForExternalFileChanges(force) to coordinate external file reloads after merge actions. Changes
Sequence DiagramsequenceDiagram
actor User
participant GitFragment as "Git Bottom Sheet\nFragment"
participant ViewModel as "GitBottomSheet\nViewModel"
participant GitRepo as "GitRepository\n(JGitRepository)"
participant Editor as "EditorHandler\nActivity"
User->>GitFragment: Initiate Pull
GitFragment->>ViewModel: pull()
ViewModel->>GitRepo: perform pull/merge
alt MergeStatus == CONFLICTING
GitRepo-->>ViewModel: MergeStatus.CONFLICTING
ViewModel-->>GitFragment: emit PullUiState.Conflicts
GitFragment->>User: show merge-conflicts dialog
User->>GitFragment: Click "Abort Merge"
GitFragment->>User: show confirm dialog
User->>GitFragment: Confirm
GitFragment->>ViewModel: abortMerge(onSuccess)
ViewModel->>GitRepo: abortMerge()
GitRepo-->>ViewModel: success
ViewModel-->>GitFragment: refreshStatus / callback
GitFragment->>Editor: checkForExternalFileChanges(force=true)
Editor-->>GitFragment: external files reloaded
else CheckoutConflictException
GitRepo-->>ViewModel: CheckoutConflictException (paths)
ViewModel-->>GitFragment: emit PullUiState.Error(errorResId, errorArgs)
GitFragment->>User: display checkout conflict message with paths
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt (1)
283-301:⚠️ Potential issue | 🟠 MajorSeed
fileTimestampsbefore using this reload path.
checkForExternalFileChanges()only refreshes files that already have an entry infileTimestamps, and Line 289 skips everything else. Right now that map is filled on save,onPause(), or a prior reload, but not when a tab is first opened. So if a pull writes conflict markers into a file that was already open in this session, reopening it from the Git sheet can still show the stale in-memory buffer instead of the conflicted disk content. Initializing the timestamp when a file is opened, or lazily backfilling missing entries before the comparison, would close that gap.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt` around lines 283 - 301, checkForExternalFileChanges() only processes files present in the fileTimestamps map; seed missing entries to avoid ignoring newly opened tabs—either initialize fileTimestamps[file.absolutePath] = file.lastModified() when a tab is opened (where the editor adds opened files in editorViewModel.getOpenedFiles()/the tab-opening routine), or add a quick backfill at the start of checkForExternalFileChanges(): for each file from editorViewModel.getOpenedFiles() if fileTimestamps[file.absolutePath] is null then set it to file.lastModified(); this ensures the later comparison (currentTimestamp > lastKnownTimestamp) will detect real on-disk changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@app/src/main/java/com/itsaky/androidide/fragments/git/GitBottomSheetFragment.kt`:
- Around line 170-175: The abort-merge callback currently only calls
EditorHandlerActivity.checkForExternalFileChanges(), which intentionally ignores
modified tabs and therefore leaves stale conflict edits; update the
viewModel.abortMerge completion handler to force-refresh conflicted editor tabs
instead of the normal external-change check: identify the conflicted file paths
from the aborted merge result (or from viewModel state) and then call
EditorHandlerActivity methods to either forceReloadTab(path) or
closeAndReopenTab(path) (implement these helpers if missing) so modified tabs
showing conflict resolution are replaced with the repository state; ensure to
call these force-refresh helpers on the UI thread after viewModel.abortMerge
completes.
In
`@app/src/main/java/com/itsaky/androidide/viewmodel/GitBottomSheetViewModel.kt`:
- Around line 288-297: The Conflicts state emitted by handlePullError should be
treated as sticky: in pull()'s finally block skip scheduling the delayed reset
when _pullState.value is PullUiState.Conflicts (e.g. wrap the existing
pullResetJob/viewModelScope.launch/delay logic in a guard like if
(_pullState.value !is PullUiState.Conflicts) { ... }), and ensure that
repository merge resolution paths (the abort/resolve handlers) explicitly set
_pullState.value = PullUiState.Idle after successful abort or resolve so the UI
is cleared only when the merge is completed.
- Around line 360-365: The abortMerge method currently calls
currentRepository?.abortMerge() and still invokes onSuccess even when
currentRepository is null; update abortMerge to check currentRepository at the
start (e.g., if (currentRepository == null) ...) and return early or invoke a
failure callback instead of calling onSuccess, ensuring you only call
currentRepository.abortMerge(), refreshStatus(), and onSuccess after confirming
currentRepository is non-null; reference the abortMerge function,
currentRepository property, refreshStatus(), and onSuccess to locate and fix the
logic.
In `@git-core/src/main/java/com/itsaky/androidide/git/core/JGitRepository.kt`:
- Around line 82-87: The isMerging check in JGitRepository.kt currently only
tests repository.repositoryState == RepositoryState.MERGING, which misses the
MERGING_RESOLVED state; update the isMerging logic (the val isMerging assignment
used to populate GitStatus) to treat RepositoryState.MERGING_RESOLVED as merging
as well (i.e., check for either MERGING or MERGING_RESOLVED) so merge UI remains
visible until the merge commit is created.
- Around line 284-287: abortMerge() currently only does a hard reset and does
not remove merge-state files; update the JGitRepository.abortMerge()
implementation to explicitly remove .git/MERGE_HEAD and .git/MERGE_MSG (and any
other MERGE_* files) from the repository directory, then perform a reset to the
pre-merge commit: attempt to resolve "ORIG_HEAD" and reset to it if present,
otherwise reset to "HEAD"; keep using git.reset().setMode(ResetType.HARD).call()
for the reset step but replace the single-step behavior with this two-step
cleanup+reset flow and ensure you reference repository.repositoryState,
abortMerge(), git.reset(), ResetType.HARD, and the ORIG_HEAD/MERGE_HEAD file
names when locating the code to change.
---
Outside diff comments:
In
`@app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt`:
- Around line 283-301: checkForExternalFileChanges() only processes files
present in the fileTimestamps map; seed missing entries to avoid ignoring newly
opened tabs—either initialize fileTimestamps[file.absolutePath] =
file.lastModified() when a tab is opened (where the editor adds opened files in
editorViewModel.getOpenedFiles()/the tab-opening routine), or add a quick
backfill at the start of checkForExternalFileChanges(): for each file from
editorViewModel.getOpenedFiles() if fileTimestamps[file.absolutePath] is null
then set it to file.lastModified(); this ensures the later comparison
(currentTimestamp > lastKnownTimestamp) will detect real on-disk changes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a6f88884-bce8-4c2e-ba3e-7f5d8b2e694a
📒 Files selected for processing (9)
app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.ktapp/src/main/java/com/itsaky/androidide/fragments/git/GitBottomSheetFragment.ktapp/src/main/java/com/itsaky/androidide/fragments/git/GitCommitHistoryDialog.ktapp/src/main/java/com/itsaky/androidide/viewmodel/GitBottomSheetViewModel.ktapp/src/main/res/layout/fragment_git_bottom_sheet.xmlgit-core/src/main/java/com/itsaky/androidide/git/core/GitRepository.ktgit-core/src/main/java/com/itsaky/androidide/git/core/JGitRepository.ktgit-core/src/main/java/com/itsaky/androidide/git/core/models/GitStatus.ktresources/src/main/res/values/strings.xml
app/src/main/java/com/itsaky/androidide/fragments/git/GitBottomSheetFragment.kt
Show resolved
Hide resolved
app/src/main/java/com/itsaky/androidide/viewmodel/GitBottomSheetViewModel.kt
Show resolved
Hide resolved
app/src/main/java/com/itsaky/androidide/viewmodel/GitBottomSheetViewModel.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@app/src/main/java/com/itsaky/androidide/fragments/git/GitBottomSheetFragment.kt`:
- Around line 53-63: The call to
EditorHandlerActivity.checkForExternalFileChanges in GitBottomSheetFragment
(inside the viewLifecycleOwner.lifecycleScope.launch before openFile and
bottomSheetViewModel.setSheetState) uses the default force=false which can skip
reloading a modified editor and cause stale content to be opened; change the
invocation to pass force=true when calling checkForExternalFileChanges so the
editor is forced to refresh before activity.openFile(File(repo.rootDir,
change.path)) executes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c153b13b-d977-421e-8b70-7567df013f73
📒 Files selected for processing (4)
app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.ktapp/src/main/java/com/itsaky/androidide/fragments/git/GitBottomSheetFragment.ktgit-core/src/main/java/com/itsaky/androidide/git/core/JGitRepository.ktresources/src/main/res/values/strings.xml
✅ Files skipped from review due to trivial changes (1)
- resources/src/main/res/values/strings.xml
🚧 Files skipped from review as they are similar to previous changes (2)
- git-core/src/main/java/com/itsaky/androidide/git/core/JGitRepository.kt
- app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt
app/src/main/java/com/itsaky/androidide/fragments/git/GitBottomSheetFragment.kt
Show resolved
Hide resolved
cf2b44d to
0bcc3e5
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@git-core/src/main/java/com/itsaky/androidide/git/core/JGitRepository.kt`:
- Around line 284-286: abortMerge currently always performs a hard reset to
Constants.ORIG_HEAD via git.reset() which can discard work when no merge is in
progress; update the abortMerge() implementation to first check
repository.repositoryState and only proceed if it equals RepositoryState.MERGING
(otherwise return immediately or throw a clear exception), and then perform the
reset/cleanup
(git.reset().setMode(ResetType.HARD).setRef(Constants.ORIG_HEAD).call()) — use
repository.repositoryState == RepositoryState.MERGING to gate the operation to
avoid touching refs/worktree when not merging.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 200bb195-eefb-4306-b768-feb45d2be22d
📒 Files selected for processing (2)
app/src/main/java/com/itsaky/androidide/fragments/git/GitBottomSheetFragment.ktgit-core/src/main/java/com/itsaky/androidide/git/core/JGitRepository.kt
| override suspend fun abortMerge(): Unit = withContext(Dispatchers.IO) { | ||
| // Reset working tree and index to HEAD | ||
| git.reset().setMode(ResetType.HARD).setRef(Constants.ORIG_HEAD).call() |
There was a problem hiding this comment.
Guard abortMerge() behind the actual merge state.
This hard-resets to ORIG_HEAD even when no merge is in progress. A stray call here can roll the repo back to an unrelated previous head and discard work. Please gate the reset/cleanup on repository.repositoryState == RepositoryState.MERGING (or fail fast) before touching refs or the worktree.
Suggested fix
override suspend fun abortMerge(): Unit = withContext(Dispatchers.IO) {
+ if (repository.repositoryState != RepositoryState.MERGING) {
+ return@withContext
+ }
+
// Reset working tree and index to HEAD
git.reset().setMode(ResetType.HARD).setRef(Constants.ORIG_HEAD).call()
// Explicitly clear merge-related files to exit the MERGING state
repository.apply {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| override suspend fun abortMerge(): Unit = withContext(Dispatchers.IO) { | |
| // Reset working tree and index to HEAD | |
| git.reset().setMode(ResetType.HARD).setRef(Constants.ORIG_HEAD).call() | |
| override suspend fun abortMerge(): Unit = withContext(Dispatchers.IO) { | |
| if (repository.repositoryState != RepositoryState.MERGING) { | |
| return@withContext | |
| } | |
| // Reset working tree and index to HEAD | |
| git.reset().setMode(ResetType.HARD).setRef(Constants.ORIG_HEAD).call() | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@git-core/src/main/java/com/itsaky/androidide/git/core/JGitRepository.kt`
around lines 284 - 286, abortMerge currently always performs a hard reset to
Constants.ORIG_HEAD via git.reset() which can discard work when no merge is in
progress; update the abortMerge() implementation to first check
repository.repositoryState and only proceed if it equals RepositoryState.MERGING
(otherwise return immediately or throw a clear exception), and then perform the
reset/cleanup
(git.reset().setMode(ResetType.HARD).setRef(Constants.ORIG_HEAD).call()) — use
repository.repositoryState == RepositoryState.MERGING to gate the operation to
avoid touching refs/worktree when not merging.