Browse Source

fix new branch creation

Nostr-Signature: 7802c9afbf005e2637282f9d06ac8130fe27bfe3a94cc67c211da51d2e9e8350 573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc 30978d6a71b4935c88ff9cd1412294d850a752977943e1aa65bcfc2290d2f2e8bbce809556849a14f0923da33b12cb53d3339741cdabab3ba949dfbb48e9cc4c
main
Silberengel 3 weeks ago
parent
commit
16484aa8e3
  1. 1
      nostr/commit-signatures.jsonl
  2. 33
      src/lib/components/RepoHeaderEnhanced.svelte
  3. 15
      src/lib/services/git/file-manager.ts
  4. 24
      src/routes/api/repos/[npub]/[repo]/branches/+server.ts
  5. 31
      src/routes/repos/[npub]/[repo]/+page.svelte

1
nostr/commit-signatures.jsonl

@ -64,3 +64,4 @@
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771845583,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","fix search and relay connections"]],"content":"Signed commit: fix search and relay connections","id":"24db15027960b244eb4c8664a3642c64684ebfef8c200250093dd047cd119e7d","sig":"561d15ae39b3bf7a5b8a67539a5cfa19d53cbaca9f904589ab7cb69e568ddf056d0d83ced4830cdfdc0b386f13c4bab930264a0f6144cbb833b187b5d452c4ae"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771845583,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","fix search and relay connections"]],"content":"Signed commit: fix search and relay connections","id":"24db15027960b244eb4c8664a3642c64684ebfef8c200250093dd047cd119e7d","sig":"561d15ae39b3bf7a5b8a67539a5cfa19d53cbaca9f904589ab7cb69e568ddf056d0d83ced4830cdfdc0b386f13c4bab930264a0f6144cbb833b187b5d452c4ae"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771847704,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"9566e4e2964d0a7b80cce1889092c4db333f89843b5d68906b3c3c568e4ba57d","sig":"8cf9166c630a8dc21bbc3dfaea4330c80c93bf7bc9e8d5d3be182fb11a3b96ea2e5969f452d3e2b309103b3e7fea8fc1aa6e5908d499d0696e9bfcd3859a8e32"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771847704,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"9566e4e2964d0a7b80cce1889092c4db333f89843b5d68906b3c3c568e4ba57d","sig":"8cf9166c630a8dc21bbc3dfaea4330c80c93bf7bc9e8d5d3be182fb11a3b96ea2e5969f452d3e2b309103b3e7fea8fc1aa6e5908d499d0696e9bfcd3859a8e32"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771849427,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"1d4e6ff4059b064d7cdd465d623a606cfcc5d0565681a34f6384463d40cc8c71","sig":"f5fe3547289e994ff1a3b191607e76d778d318ca4538e70253406867ecef214c1be437dca373f9a461c9cf2ca2978a581b54a9d323baeb2c91851e9cc6ffbfd6"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771849427,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"1d4e6ff4059b064d7cdd465d623a606cfcc5d0565681a34f6384463d40cc8c71","sig":"f5fe3547289e994ff1a3b191607e76d778d318ca4538e70253406867ecef214c1be437dca373f9a461c9cf2ca2978a581b54a9d323baeb2c91851e9cc6ffbfd6"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771850840,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","rearrange repo pages"]],"content":"Signed commit: rearrange repo pages","id":"9f8b68f36189073807510a2dac268b466629ecbc6b8dca66ba809cbf3a36dab5","sig":"911debb546c23038bbf77a57bee089130c7cce3a51f2cfb385c3904ec39bc76b90dc9bef2e8e501824ecff13925523d802b6c916d07fef2718554f4f65e6f4d2"}

33
src/lib/components/RepoHeaderEnhanced.svelte

@ -241,6 +241,20 @@
Create New Branch Create New Branch
</button> </button>
{/if} {/if}
{#if isMaintainer && currentBranch && currentBranch !== defaultBranch && onDeleteBranch}
<button
class="menu-item menu-item-danger"
onclick={() => {
if (currentBranch) {
onDeleteBranch(currentBranch);
}
showMoreMenu = false;
}}
title="Delete branch"
>
Delete Branch
</button>
{/if}
{#if onCopyEventId} {#if onCopyEventId}
<button class="menu-item" onclick={() => { onCopyEventId(); showMoreMenu = false; }}> <button class="menu-item" onclick={() => { onCopyEventId(); showMoreMenu = false; }}>
Copy Event ID Copy Event ID
@ -357,13 +371,19 @@
</div> </div>
{/if} {/if}
{#if currentBranch} {#if branches.length === 0}
<div class="repo-branch">
<div class="branch-button" style="opacity: 0.6; cursor: not-allowed;">
<img src="/icons/git-branch.svg" alt="" class="icon" />
No branches
</div>
</div>
{:else if currentBranch}
<div class="repo-branch"> <div class="repo-branch">
<button <button
class="branch-button" class="branch-button"
onclick={() => showBranchMenu = !showBranchMenu} onclick={() => showBranchMenu = !showBranchMenu}
aria-expanded={showBranchMenu} aria-expanded={showBranchMenu}
disabled={branches.length === 0}
> >
<img src="/icons/git-branch.svg" alt="" class="icon" /> <img src="/icons/git-branch.svg" alt="" class="icon" />
{currentBranch} {currentBranch}
@ -388,15 +408,6 @@
{/each} {/each}
</div> </div>
{/if} {/if}
{#if isMaintainer && currentBranch && currentBranch !== defaultBranch && onDeleteBranch}
<button
class="delete-branch-button"
onclick={() => currentBranch && onDeleteBranch(currentBranch)}
title="Delete branch"
>
×
</button>
{/if}
</div> </div>
{/if} {/if}
</div> </div>

15
src/lib/services/git/file-manager.ts

@ -1765,15 +1765,12 @@ export class FileManager {
npub: string, npub: string,
repoName: string, repoName: string,
branchName: string, branchName: string,
fromBranch: string = 'main' fromBranch?: string
): Promise<void> { ): Promise<void> {
// Security: Validate branch names to prevent path traversal // Security: Validate branch names to prevent path traversal
if (!isValidBranchName(branchName)) { if (!isValidBranchName(branchName)) {
throw new Error(`Invalid branch name: ${branchName}`); throw new Error(`Invalid branch name: ${branchName}`);
} }
if (!isValidBranchName(fromBranch)) {
throw new Error(`Invalid source branch name: ${fromBranch}`);
}
const repoPath = this.getRepoPath(npub, repoName); const repoPath = this.getRepoPath(npub, repoName);
@ -1870,8 +1867,16 @@ export class FileManager {
await this.removeWorktree(repoPath, worktreePath); await this.removeWorktree(repoPath, worktreePath);
} else { } else {
// Repo has branches - use normal branch creation // Repo has branches - use normal branch creation
// Validate fromBranch if provided
if (fromBranch && !isValidBranchName(fromBranch)) {
throw new Error(`Invalid source branch name: ${fromBranch}`);
}
// Use default branch if fromBranch not provided
const sourceBranch = fromBranch || await this.getDefaultBranch(npub, repoName).catch(() => 'main');
// Use git worktree instead of cloning (much more efficient) // Use git worktree instead of cloning (much more efficient)
const workDir = await this.getWorktree(repoPath, fromBranch, npub, repoName); const workDir = await this.getWorktree(repoPath, sourceBranch, npub, repoName);
const workGit: SimpleGit = simpleGit(workDir); const workGit: SimpleGit = simpleGit(workDir);
// Create and checkout new branch // Create and checkout new branch

24
src/routes/api/repos/[npub]/[repo]/branches/+server.ts

@ -244,18 +244,30 @@ export const POST: RequestHandler = createRepoPostHandler(
} }
} }
// Get default branch if fromBranch not provided // Check if repo has any branches first
// If repo has no branches, use 'master' as default let hasBranches = false;
try {
const existingBranches = await fileManager.getBranches(context.npub, context.repo);
hasBranches = existingBranches.length > 0;
} catch (err) {
// If getBranches fails, assume no branches exist
logger.debug({ error: err, npub: context.npub, repo: context.repo }, 'Failed to get branches, assuming empty repo');
hasBranches = false;
}
// Get default branch if fromBranch not provided and repo has branches
// If repo has no branches, don't pass fromBranch (will use --orphan)
let sourceBranch = fromBranch; let sourceBranch = fromBranch;
if (!sourceBranch) { if (!sourceBranch && hasBranches) {
try { try {
sourceBranch = await fileManager.getDefaultBranch(context.npub, context.repo); sourceBranch = await fileManager.getDefaultBranch(context.npub, context.repo);
} catch (err) { } catch (err) {
// If getDefaultBranch fails (e.g., no branches exist), use 'master' as default // If getDefaultBranch fails, use 'main' as default (only if branches exist)
logger.debug({ error: err, npub: context.npub, repo: context.repo }, 'No default branch found, using master'); logger.debug({ error: err, npub: context.npub, repo: context.repo }, 'No default branch found, using main');
sourceBranch = 'master'; sourceBranch = 'main';
} }
} }
// If repo has no branches, sourceBranch will be undefined/null, which createBranch will handle correctly
await fileManager.createBranch(context.npub, context.repo, branchName, sourceBranch); await fileManager.createBranch(context.npub, context.repo, branchName, sourceBranch);
return json({ success: true, message: 'Branch created successfully' }); return json({ success: true, message: 'Branch created successfully' });

31
src/routes/repos/[npub]/[repo]/+page.svelte

@ -2806,15 +2806,8 @@
currentBranch = defaultBranch; currentBranch = defaultBranch;
} }
} else { } else {
// No branches loaded yet or empty repo - set currentBranch from settings if not set // No branches exist - set currentBranch to null to show "no branches" in header
if (!currentBranch) { currentBranch = null;
try {
const settings = await settingsStore.getSettings();
currentBranch = settings.defaultBranch || 'master';
} catch {
currentBranch = 'master';
}
}
} }
} else if (response.status === 404) { } else if (response.status === 404) {
// Check if this is a "not cloned" error - API fallback might be available // Check if this is a "not cloned" error - API fallback might be available
@ -3803,11 +3796,16 @@
error = null; error = null;
try { try {
// If no branches exist, use default branch from settings // If no branches exist, don't pass fromBranch (will use --orphan)
let fromBranch = newBranchFrom || currentBranch; // Otherwise, use the selected branch or current branch
if (!fromBranch && branches.length === 0) { let fromBranch: string | undefined = newBranchFrom || currentBranch || undefined;
const settings = await settingsStore.getSettings();
fromBranch = settings.defaultBranch || 'master'; // Only include fromBranch if repo has branches
const requestBody: { branchName: string; fromBranch?: string } = {
branchName: newBranchName
};
if (branches.length > 0 && fromBranch) {
requestBody.fromBranch = fromBranch;
} }
const response = await fetch(`/api/repos/${npub}/${repo}/branches`, { const response = await fetch(`/api/repos/${npub}/${repo}/branches`, {
@ -3816,10 +3814,7 @@
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...buildApiHeaders() ...buildApiHeaders()
}, },
body: JSON.stringify({ body: JSON.stringify(requestBody)
branchName: newBranchName,
fromBranch: fromBranch || 'master' // Final fallback
})
}); });
if (!response.ok) { if (!response.ok) {

Loading…
Cancel
Save