feat(git): add isFileUncommitted utility and update cancelOutCreate logic

fix(repo): change pull command to standard without rebase
fix(changes): ensure UI refresh after discarding and adding changes
fix(delay-profile): correct label structure for tags in DelayProfileForm
This commit is contained in:
Sam Chau
2025-12-29 01:35:50 +10:30
parent 7db49af4a2
commit 4aa1c0c8e3
5 changed files with 43 additions and 11 deletions

View File

@@ -7,6 +7,7 @@ import { getBaseOpsPath, getUserOpsPath } from './ops.ts';
import { databaseInstancesQueries } from '$db/queries/databaseInstances.ts';
import { logger } from '$logger/logger.ts';
import { compile } from './cache.ts';
import { isFileUncommitted } from '$utils/git/status.ts';
export type OperationLayer = 'base' | 'user';
export type OperationType = 'create' | 'update' | 'delete';
@@ -176,9 +177,11 @@ async function parseOperationMetadata(filepath: string): Promise<OperationMetada
/**
* Find and remove a matching create operation for a delete
* Returns true if a create was found and removed (no delete needed)
* Only cancels out if the create file is uncommitted (untracked or staged)
*/
async function cancelOutCreate(
targetDir: string,
repoPath: string,
metadata: OperationMetadata
): Promise<boolean> {
if (metadata.operation !== 'delete') {
@@ -199,9 +202,16 @@ async function cancelOutCreate(
fileMeta.entity === metadata.entity &&
fileMeta.name === metadata.name
) {
// Only cancel out if the file is uncommitted
const uncommitted = await isFileUncommitted(repoPath, filepath);
if (!uncommitted) {
// File has been committed - don't cancel, proceed with delete operation
continue;
}
// Remove the create file - it cancels out with the delete
await Deno.remove(filepath);
await logger.info('Cancelled out create operation with delete', {
await logger.info('Cancelled out uncommitted create operation with delete', {
source: 'PCDWriter',
meta: { filepath, entity: metadata.entity, name: metadata.name }
});
@@ -245,7 +255,7 @@ export async function writeOperation(options: WriteOptions): Promise<WriteResult
await ensureDir(targetDir);
// Optimization: if this is a delete and there's an uncommitted create, just remove the create
if (metadata && await cancelOutCreate(targetDir, metadata)) {
if (metadata && await cancelOutCreate(targetDir, instance.local_path, metadata)) {
// Recompile the cache after removing the create file
await compile(instance.local_path, instance.id);
return { success: true };

View File

@@ -115,10 +115,10 @@ export async function fetch(repoPath: string): Promise<void> {
}
/**
* Pull with rebase
* Pull from remote
*/
export async function pull(repoPath: string): Promise<void> {
await execGit(['pull', '--rebase'], repoPath);
await execGit(['pull'], repoPath);
}
/**

View File

@@ -133,6 +133,23 @@ export async function getBranches(repoPath: string): Promise<string[]> {
return branches;
}
/**
* Check if a file is uncommitted (untracked or staged but not yet committed)
*/
export async function isFileUncommitted(repoPath: string, filepath: string): Promise<boolean> {
// Get relative path from repo root
const relativePath = filepath.startsWith(repoPath + '/')
? filepath.slice(repoPath.length + 1)
: filepath;
const output = await execGitSafe(['status', '--porcelain', relativePath], repoPath);
if (!output) return false;
const status = output.substring(0, 2);
// ?? = untracked, A = added (staged), AM = added and modified
return status.startsWith('??') || status[0] === 'A';
}
/**
* Get commit history
*/

View File

@@ -66,18 +66,20 @@
const response = await fetch('?/discard', {
method: 'POST',
body: formData
body: formData,
headers: { 'Accept': 'application/json' }
});
const result = await response.json();
if (result.type === 'success' && result.data?.success) {
selected = new Set();
alertStore.add('success', 'Changes discarded');
await fetchChanges();
} else {
alertStore.add('error', result.data?.error || 'Failed to discard changes');
}
selected = new Set();
await fetchChanges();
}
async function handleAdd() {
@@ -89,18 +91,21 @@
const response = await fetch('?/add', {
method: 'POST',
body: formData
body: formData,
headers: { 'Accept': 'application/json' }
});
const result = await response.json();
if (result.type === 'success' && result.data?.success) {
commitMessage = '';
alertStore.add('success', 'Changes committed and pushed');
} else {
alertStore.add('error', result.data?.error || 'Failed to add changes');
}
// Always clear and refresh
commitMessage = '';
// Always refresh to keep UI in sync with file system
selected = new Set();
await fetchChanges();

View File

@@ -161,9 +161,9 @@
<!-- Tags -->
<div>
<label class="block text-sm font-medium text-neutral-700 dark:text-neutral-300">
<div class="block text-sm font-medium text-neutral-700 dark:text-neutral-300">
Tags <span class="text-red-500">*</span>
</label>
</div>
<p class="mt-1 text-xs text-neutral-500 dark:text-neutral-400">
Delay profiles apply to items with matching tags
</p>