Added pull and fetch on git panel

Made UX enhancements
This commit is contained in:
simos
2025-07-12 22:02:59 +00:00
parent 00acc57161
commit 7329f89c96
3 changed files with 592 additions and 88 deletions

View File

@@ -420,4 +420,167 @@ function generateSimpleCommitMessage(files, diff) {
}
}
// Get remote status (ahead/behind commits with smart remote detection)
router.get('/remote-status', async (req, res) => {
const { project } = req.query;
if (!project) {
return res.status(400).json({ error: 'Project name is required' });
}
try {
const projectPath = await getActualProjectPath(project);
await validateGitRepository(projectPath);
// Get current branch
const { stdout: currentBranch } = await execAsync('git rev-parse --abbrev-ref HEAD', { cwd: projectPath });
const branch = currentBranch.trim();
// Check if there's a remote tracking branch (smart detection)
let trackingBranch;
let remoteName;
try {
const { stdout } = await execAsync(`git rev-parse --abbrev-ref ${branch}@{upstream}`, { cwd: projectPath });
trackingBranch = stdout.trim();
remoteName = trackingBranch.split('/')[0]; // Extract remote name (e.g., "origin/main" -> "origin")
} catch (error) {
// No upstream branch configured
return res.json({
hasRemote: false,
branch,
message: 'No remote tracking branch configured'
});
}
// Get ahead/behind counts
const { stdout: countOutput } = await execAsync(
`git rev-list --count --left-right ${trackingBranch}...HEAD`,
{ cwd: projectPath }
);
const [behind, ahead] = countOutput.trim().split('\t').map(Number);
res.json({
hasRemote: true,
branch,
remoteBranch: trackingBranch,
remoteName,
ahead: ahead || 0,
behind: behind || 0,
isUpToDate: ahead === 0 && behind === 0
});
} catch (error) {
console.error('Git remote status error:', error);
res.json({ error: error.message });
}
});
// Fetch from remote (using smart remote detection)
router.post('/fetch', async (req, res) => {
const { project } = req.body;
if (!project) {
return res.status(400).json({ error: 'Project name is required' });
}
try {
const projectPath = await getActualProjectPath(project);
await validateGitRepository(projectPath);
// Get current branch and its upstream remote
const { stdout: currentBranch } = await execAsync('git rev-parse --abbrev-ref HEAD', { cwd: projectPath });
const branch = currentBranch.trim();
let remoteName = 'origin'; // fallback
try {
const { stdout } = await execAsync(`git rev-parse --abbrev-ref ${branch}@{upstream}`, { cwd: projectPath });
remoteName = stdout.trim().split('/')[0]; // Extract remote name
} catch (error) {
// No upstream, try to fetch from origin anyway
console.log('No upstream configured, using origin as fallback');
}
const { stdout } = await execAsync(`git fetch ${remoteName}`, { cwd: projectPath });
res.json({ success: true, output: stdout || 'Fetch completed successfully', remoteName });
} catch (error) {
console.error('Git fetch error:', error);
res.status(500).json({
error: 'Fetch failed',
details: error.message.includes('Could not resolve hostname')
? 'Unable to connect to remote repository. Check your internet connection.'
: error.message.includes('fatal: \'origin\' does not appear to be a git repository')
? 'No remote repository configured. Add a remote with: git remote add origin <url>'
: error.message
});
}
});
// Pull from remote (fetch + merge using smart remote detection)
router.post('/pull', async (req, res) => {
const { project } = req.body;
if (!project) {
return res.status(400).json({ error: 'Project name is required' });
}
try {
const projectPath = await getActualProjectPath(project);
await validateGitRepository(projectPath);
// Get current branch and its upstream remote
const { stdout: currentBranch } = await execAsync('git rev-parse --abbrev-ref HEAD', { cwd: projectPath });
const branch = currentBranch.trim();
let remoteName = 'origin'; // fallback
let remoteBranch = branch; // fallback
try {
const { stdout } = await execAsync(`git rev-parse --abbrev-ref ${branch}@{upstream}`, { cwd: projectPath });
const tracking = stdout.trim();
remoteName = tracking.split('/')[0]; // Extract remote name
remoteBranch = tracking.split('/').slice(1).join('/'); // Extract branch name
} catch (error) {
// No upstream, use fallback
console.log('No upstream configured, using origin/branch as fallback');
}
const { stdout } = await execAsync(`git pull ${remoteName} ${remoteBranch}`, { cwd: projectPath });
res.json({
success: true,
output: stdout || 'Pull completed successfully',
remoteName,
remoteBranch
});
} catch (error) {
console.error('Git pull error:', error);
// Enhanced error handling for common pull scenarios
let errorMessage = 'Pull failed';
let details = error.message;
if (error.message.includes('CONFLICT')) {
errorMessage = 'Merge conflicts detected';
details = 'Pull created merge conflicts. Please resolve conflicts manually in the editor, then commit the changes.';
} else if (error.message.includes('Please commit your changes or stash them')) {
errorMessage = 'Uncommitted changes detected';
details = 'Please commit or stash your local changes before pulling.';
} else if (error.message.includes('Could not resolve hostname')) {
errorMessage = 'Network error';
details = 'Unable to connect to remote repository. Check your internet connection.';
} else if (error.message.includes('fatal: \'origin\' does not appear to be a git repository')) {
errorMessage = 'Remote not configured';
details = 'No remote repository configured. Add a remote with: git remote add origin <url>';
} else if (error.message.includes('diverged')) {
errorMessage = 'Branches have diverged';
details = 'Your local branch and remote branch have diverged. Consider fetching first to review changes.';
}
res.status(500).json({
error: errorMessage,
details: details
});
}
});
export default router;