claude-code-templates 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/command-scanner.js +175 -0
- package/src/file-operations.js +34 -2
- package/src/prompts.js +47 -1
- package/src/templates.js +10 -1
- package/templates/common/.claude/commands/git-workflow.md +239 -0
- package/templates/common/.claude/commands/project-setup.md +316 -0
- package/templates/javascript-typescript/.claude/commands/route.md +193 -0
- package/templates/python/.claude/commands/django-model.md +124 -0
- package/templates/python/.claude/commands/flask-route.md +217 -0
- package/templates/python/.claude/commands/lint.md +111 -0
- package/templates/python/.claude/commands/test.md +73 -0
package/package.json
CHANGED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Scans and returns available commands for a given language
|
|
6
|
+
* @param {string} language - The language template to scan
|
|
7
|
+
* @returns {Array} Array of available commands with metadata
|
|
8
|
+
*/
|
|
9
|
+
function getAvailableCommands(language) {
|
|
10
|
+
const templatesDir = path.join(__dirname, '..', 'templates');
|
|
11
|
+
const languageDir = path.join(templatesDir, language);
|
|
12
|
+
|
|
13
|
+
// Check if language directory exists
|
|
14
|
+
if (!fs.existsSync(languageDir)) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const commands = [];
|
|
19
|
+
|
|
20
|
+
// Scan main .claude/commands directory
|
|
21
|
+
const mainCommandsDir = path.join(languageDir, '.claude', 'commands');
|
|
22
|
+
if (fs.existsSync(mainCommandsDir)) {
|
|
23
|
+
const mainCommands = scanCommandsInDirectory(mainCommandsDir, 'core');
|
|
24
|
+
commands.push(...mainCommands);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Scan framework-specific commands in examples
|
|
28
|
+
const examplesDir = path.join(languageDir, 'examples');
|
|
29
|
+
if (fs.existsSync(examplesDir)) {
|
|
30
|
+
const frameworkDirs = fs.readdirSync(examplesDir).filter(dir => {
|
|
31
|
+
return fs.statSync(path.join(examplesDir, dir)).isDirectory();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
frameworkDirs.forEach(framework => {
|
|
35
|
+
const frameworkCommandsDir = path.join(examplesDir, framework, '.claude', 'commands');
|
|
36
|
+
if (fs.existsSync(frameworkCommandsDir)) {
|
|
37
|
+
const frameworkCommands = scanCommandsInDirectory(frameworkCommandsDir, framework);
|
|
38
|
+
commands.push(...frameworkCommands);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Remove duplicates based on command name
|
|
44
|
+
const uniqueCommands = commands.reduce((acc, command) => {
|
|
45
|
+
const existing = acc.find(c => c.name === command.name);
|
|
46
|
+
if (!existing) {
|
|
47
|
+
acc.push(command);
|
|
48
|
+
} else {
|
|
49
|
+
// If duplicate, prefer core commands over framework-specific ones
|
|
50
|
+
if (command.category === 'core' && existing.category !== 'core') {
|
|
51
|
+
const index = acc.findIndex(c => c.name === command.name);
|
|
52
|
+
acc[index] = command;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return acc;
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
58
|
+
return uniqueCommands.sort((a, b) => {
|
|
59
|
+
// Sort by category first (core first), then by name
|
|
60
|
+
if (a.category !== b.category) {
|
|
61
|
+
return a.category === 'core' ? -1 : 1;
|
|
62
|
+
}
|
|
63
|
+
return a.name.localeCompare(b.name);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Scans a directory for command files and returns command metadata
|
|
69
|
+
* @param {string} commandsDir - Directory to scan
|
|
70
|
+
* @param {string} category - Category of commands (core, react-app, node-api, etc.)
|
|
71
|
+
* @returns {Array} Array of command objects
|
|
72
|
+
*/
|
|
73
|
+
function scanCommandsInDirectory(commandsDir, category) {
|
|
74
|
+
const commands = [];
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const files = fs.readdirSync(commandsDir);
|
|
78
|
+
|
|
79
|
+
files.forEach(file => {
|
|
80
|
+
if (path.extname(file) === '.md') {
|
|
81
|
+
const commandName = path.basename(file, '.md');
|
|
82
|
+
const filePath = path.join(commandsDir, file);
|
|
83
|
+
|
|
84
|
+
// Read the command file to extract metadata
|
|
85
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
86
|
+
const metadata = parseCommandMetadata(content, commandName);
|
|
87
|
+
|
|
88
|
+
commands.push({
|
|
89
|
+
name: commandName,
|
|
90
|
+
displayName: metadata.title || commandName,
|
|
91
|
+
description: metadata.description || `${commandName} command`,
|
|
92
|
+
category: category,
|
|
93
|
+
filePath: filePath,
|
|
94
|
+
checked: metadata.defaultChecked || false
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.warn(`Warning: Could not scan commands in ${commandsDir}:`, error.message);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return commands;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Parses command metadata from markdown file content
|
|
107
|
+
* @param {string} content - The markdown content
|
|
108
|
+
* @param {string} commandName - The command name
|
|
109
|
+
* @returns {Object} Parsed metadata
|
|
110
|
+
*/
|
|
111
|
+
function parseCommandMetadata(content, commandName) {
|
|
112
|
+
const metadata = {
|
|
113
|
+
title: null,
|
|
114
|
+
description: null,
|
|
115
|
+
defaultChecked: false
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const lines = content.split('\n');
|
|
119
|
+
|
|
120
|
+
// Extract title from first heading
|
|
121
|
+
for (let i = 0; i < Math.min(lines.length, 10); i++) {
|
|
122
|
+
const line = lines[i].trim();
|
|
123
|
+
if (line.startsWith('# ')) {
|
|
124
|
+
metadata.title = line.substring(2).trim();
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Extract description from purpose section or first paragraph
|
|
130
|
+
const purposeMatch = content.match(/## Purpose\s*\n\s*(.+?)(?=\n\n|\n##|$)/s);
|
|
131
|
+
if (purposeMatch) {
|
|
132
|
+
metadata.description = purposeMatch[1].trim().replace(/\n/g, ' ');
|
|
133
|
+
} else {
|
|
134
|
+
// Try to find first paragraph after title
|
|
135
|
+
const paragraphMatch = content.match(/^#[^\n]*\n\s*\n(.+?)(?=\n\n|\n##|$)/s);
|
|
136
|
+
if (paragraphMatch) {
|
|
137
|
+
metadata.description = paragraphMatch[1].trim().replace(/\n/g, ' ');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Determine default checked state (core commands usually checked by default)
|
|
142
|
+
const coreCommands = ['test', 'lint', 'debug', 'refactor'];
|
|
143
|
+
metadata.defaultChecked = coreCommands.includes(commandName);
|
|
144
|
+
|
|
145
|
+
return metadata;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get commands available for a specific language and framework combination
|
|
150
|
+
* @param {string} language - The language template
|
|
151
|
+
* @param {string} framework - The framework (optional)
|
|
152
|
+
* @returns {Array} Array of available commands
|
|
153
|
+
*/
|
|
154
|
+
function getCommandsForLanguageAndFramework(language, framework = null) {
|
|
155
|
+
const allCommands = getAvailableCommands(language);
|
|
156
|
+
|
|
157
|
+
if (!framework || framework === 'none') {
|
|
158
|
+
// Return only core commands
|
|
159
|
+
return allCommands.filter(cmd => cmd.category === 'core');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Return core commands + framework-specific commands
|
|
163
|
+
return allCommands.filter(cmd =>
|
|
164
|
+
cmd.category === 'core' ||
|
|
165
|
+
cmd.category === framework ||
|
|
166
|
+
cmd.category === `${framework}-app`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
module.exports = {
|
|
171
|
+
getAvailableCommands,
|
|
172
|
+
getCommandsForLanguageAndFramework,
|
|
173
|
+
scanCommandsInDirectory,
|
|
174
|
+
parseCommandMetadata
|
|
175
|
+
};
|
package/src/file-operations.js
CHANGED
|
@@ -23,19 +23,51 @@ async function copyTemplateFiles(templateConfig, targetDir) {
|
|
|
23
23
|
console.log(chalk.yellow(`📁 Existing .claude directory backed up to .claude.backup`));
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
// Copy files
|
|
26
|
+
// Copy base files (but skip .claude/commands for now)
|
|
27
27
|
for (const file of templateConfig.files) {
|
|
28
28
|
const sourcePath = path.join(templateDir, file.source);
|
|
29
29
|
const destPath = path.join(targetDir, file.destination);
|
|
30
30
|
|
|
31
31
|
try {
|
|
32
|
-
|
|
32
|
+
// Skip .claude/commands directories - we'll handle them separately
|
|
33
|
+
if (file.source.includes('.claude/commands')) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
await fs.copy(sourcePath, destPath, {
|
|
38
|
+
overwrite: true,
|
|
39
|
+
filter: (src) => {
|
|
40
|
+
// Skip commands directory during base copy
|
|
41
|
+
return !src.includes('.claude/commands');
|
|
42
|
+
}
|
|
43
|
+
});
|
|
33
44
|
console.log(chalk.green(`✓ Copied ${file.source} → ${file.destination}`));
|
|
34
45
|
} catch (error) {
|
|
35
46
|
console.error(chalk.red(`✗ Failed to copy ${file.source}:`), error.message);
|
|
36
47
|
throw error;
|
|
37
48
|
}
|
|
38
49
|
}
|
|
50
|
+
|
|
51
|
+
// Copy selected commands individually
|
|
52
|
+
if (templateConfig.selectedCommands && templateConfig.selectedCommands.length > 0) {
|
|
53
|
+
const commandsDir = path.join(targetDir, '.claude', 'commands');
|
|
54
|
+
await fs.ensureDir(commandsDir);
|
|
55
|
+
|
|
56
|
+
for (const command of templateConfig.selectedCommands) {
|
|
57
|
+
try {
|
|
58
|
+
const commandFileName = `${command.name}.md`;
|
|
59
|
+
const destPath = path.join(commandsDir, commandFileName);
|
|
60
|
+
|
|
61
|
+
await fs.copy(command.filePath, destPath);
|
|
62
|
+
console.log(chalk.green(`✓ Added command: ${command.displayName}`));
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error(chalk.red(`✗ Failed to copy command ${command.name}:`), error.message);
|
|
65
|
+
// Don't throw - continue with other commands
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log(chalk.cyan(`📋 Installed ${templateConfig.selectedCommands.length} commands`));
|
|
70
|
+
}
|
|
39
71
|
}
|
|
40
72
|
|
|
41
73
|
async function ensureDirectoryExists(dirPath) {
|
package/src/prompts.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const { getAvailableLanguages, getFrameworksForLanguage } = require('./templates');
|
|
3
|
+
const { getCommandsForLanguageAndFramework } = require('./command-scanner');
|
|
3
4
|
|
|
4
5
|
function createPrompts(projectInfo, options = {}) {
|
|
5
6
|
const prompts = [];
|
|
@@ -41,6 +42,40 @@ function createPrompts(projectInfo, options = {}) {
|
|
|
41
42
|
});
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
// Command selection
|
|
46
|
+
prompts.push({
|
|
47
|
+
type: 'checkbox',
|
|
48
|
+
name: 'commands',
|
|
49
|
+
message: 'Select commands to include (use space to select):',
|
|
50
|
+
choices: (answers) => {
|
|
51
|
+
const selectedLanguage = answers.language || options.language;
|
|
52
|
+
const selectedFramework = answers.framework || options.framework;
|
|
53
|
+
|
|
54
|
+
if (!selectedLanguage || selectedLanguage === 'common') {
|
|
55
|
+
return [
|
|
56
|
+
{
|
|
57
|
+
value: 'basic-commands',
|
|
58
|
+
name: 'Basic development commands',
|
|
59
|
+
checked: true
|
|
60
|
+
}
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const availableCommands = getCommandsForLanguageAndFramework(selectedLanguage, selectedFramework);
|
|
65
|
+
|
|
66
|
+
return availableCommands.map(cmd => ({
|
|
67
|
+
value: cmd.name,
|
|
68
|
+
name: `${cmd.displayName} - ${cmd.description}`,
|
|
69
|
+
checked: cmd.checked
|
|
70
|
+
}));
|
|
71
|
+
},
|
|
72
|
+
prefix: chalk.cyan('📋'),
|
|
73
|
+
when: (answers) => {
|
|
74
|
+
const selectedLanguage = answers.language || options.language;
|
|
75
|
+
return selectedLanguage && selectedLanguage !== 'common';
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
44
79
|
// Features selection
|
|
45
80
|
prompts.push({
|
|
46
81
|
type: 'checkbox',
|
|
@@ -78,7 +113,18 @@ function createPrompts(projectInfo, options = {}) {
|
|
|
78
113
|
message: (answers) => {
|
|
79
114
|
const language = answers.language || options.language || 'common';
|
|
80
115
|
const framework = answers.framework || options.framework || 'none';
|
|
81
|
-
|
|
116
|
+
const commandCount = answers.commands ? answers.commands.length : 0;
|
|
117
|
+
|
|
118
|
+
let message = `Setup Claude Code for ${chalk.cyan(language)}`;
|
|
119
|
+
if (framework !== 'none') {
|
|
120
|
+
message += ` with ${chalk.green(framework)}`;
|
|
121
|
+
}
|
|
122
|
+
if (commandCount > 0) {
|
|
123
|
+
message += ` (${chalk.yellow(commandCount)} commands)`;
|
|
124
|
+
}
|
|
125
|
+
message += '?';
|
|
126
|
+
|
|
127
|
+
return message;
|
|
82
128
|
},
|
|
83
129
|
default: true,
|
|
84
130
|
prefix: chalk.red('🚀')
|
package/src/templates.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
+
const { getCommandsForLanguageAndFramework } = require('./command-scanner');
|
|
2
3
|
|
|
3
4
|
const TEMPLATES_CONFIG = {
|
|
4
5
|
'common': {
|
|
@@ -101,7 +102,7 @@ function getFrameworksForLanguage(language) {
|
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
function getTemplateConfig(selections) {
|
|
104
|
-
const { language, framework } = selections;
|
|
105
|
+
const { language, framework, commands = [] } = selections;
|
|
105
106
|
const baseConfig = TEMPLATES_CONFIG[language];
|
|
106
107
|
|
|
107
108
|
if (!baseConfig) {
|
|
@@ -118,10 +119,18 @@ function getTemplateConfig(selections) {
|
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
121
|
|
|
122
|
+
// Handle command selection
|
|
123
|
+
let selectedCommands = [];
|
|
124
|
+
if (commands && commands.length > 0) {
|
|
125
|
+
const availableCommands = getCommandsForLanguageAndFramework(language, framework);
|
|
126
|
+
selectedCommands = availableCommands.filter(cmd => commands.includes(cmd.name));
|
|
127
|
+
}
|
|
128
|
+
|
|
121
129
|
return {
|
|
122
130
|
language,
|
|
123
131
|
framework,
|
|
124
132
|
files,
|
|
133
|
+
selectedCommands,
|
|
125
134
|
config: baseConfig
|
|
126
135
|
};
|
|
127
136
|
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Git Workflow Helper
|
|
2
|
+
|
|
3
|
+
Manage Git workflows with best practices and common operations.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
This command helps you perform common Git operations following best practices for collaborative development.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
/git-workflow
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## What this command does
|
|
16
|
+
|
|
17
|
+
1. **Guides through Git operations** with proper workflow
|
|
18
|
+
2. **Suggests best practices** for commits and branches
|
|
19
|
+
3. **Helps with conflict resolution** and merging
|
|
20
|
+
4. **Provides templates** for commit messages
|
|
21
|
+
5. **Manages branching strategies** (Git Flow, GitHub Flow)
|
|
22
|
+
|
|
23
|
+
## Common Workflows
|
|
24
|
+
|
|
25
|
+
### Feature Development
|
|
26
|
+
```bash
|
|
27
|
+
# Create and switch to feature branch
|
|
28
|
+
git checkout -b feature/user-authentication
|
|
29
|
+
|
|
30
|
+
# Work on your feature
|
|
31
|
+
# ... make changes ...
|
|
32
|
+
|
|
33
|
+
# Stage and commit changes
|
|
34
|
+
git add .
|
|
35
|
+
git commit -m "feat: add user authentication system
|
|
36
|
+
|
|
37
|
+
- Implement login/logout functionality
|
|
38
|
+
- Add password validation
|
|
39
|
+
- Create user session management
|
|
40
|
+
- Add authentication middleware"
|
|
41
|
+
|
|
42
|
+
# Push feature branch
|
|
43
|
+
git push -u origin feature/user-authentication
|
|
44
|
+
|
|
45
|
+
# Create pull request (via GitHub/GitLab interface)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Hotfix Workflow
|
|
49
|
+
```bash
|
|
50
|
+
# Create hotfix branch from main
|
|
51
|
+
git checkout main
|
|
52
|
+
git checkout -b hotfix/security-patch
|
|
53
|
+
|
|
54
|
+
# Fix the issue
|
|
55
|
+
# ... make changes ...
|
|
56
|
+
|
|
57
|
+
# Commit the fix
|
|
58
|
+
git commit -m "fix: resolve security vulnerability in auth module
|
|
59
|
+
|
|
60
|
+
- Patch XSS vulnerability in login form
|
|
61
|
+
- Update input validation
|
|
62
|
+
- Add CSRF protection"
|
|
63
|
+
|
|
64
|
+
# Push and create urgent PR
|
|
65
|
+
git push -u origin hotfix/security-patch
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Sync with Remote
|
|
69
|
+
```bash
|
|
70
|
+
# Update main branch
|
|
71
|
+
git checkout main
|
|
72
|
+
git pull origin main
|
|
73
|
+
|
|
74
|
+
# Update feature branch with latest main
|
|
75
|
+
git checkout feature/your-feature
|
|
76
|
+
git rebase main
|
|
77
|
+
# OR
|
|
78
|
+
git merge main
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Commit Message Conventions
|
|
82
|
+
|
|
83
|
+
### Conventional Commits Format
|
|
84
|
+
```
|
|
85
|
+
<type>[optional scope]: <description>
|
|
86
|
+
|
|
87
|
+
[optional body]
|
|
88
|
+
|
|
89
|
+
[optional footer(s)]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Common Types
|
|
93
|
+
- **feat**: New feature
|
|
94
|
+
- **fix**: Bug fix
|
|
95
|
+
- **docs**: Documentation changes
|
|
96
|
+
- **style**: Code style changes (formatting, etc.)
|
|
97
|
+
- **refactor**: Code refactoring
|
|
98
|
+
- **test**: Adding or updating tests
|
|
99
|
+
- **chore**: Maintenance tasks
|
|
100
|
+
|
|
101
|
+
### Examples
|
|
102
|
+
```bash
|
|
103
|
+
# Feature
|
|
104
|
+
git commit -m "feat(auth): add OAuth2 integration"
|
|
105
|
+
|
|
106
|
+
# Bug fix
|
|
107
|
+
git commit -m "fix(api): handle null response in user endpoint"
|
|
108
|
+
|
|
109
|
+
# Documentation
|
|
110
|
+
git commit -m "docs: update API documentation for v2.0"
|
|
111
|
+
|
|
112
|
+
# Breaking change
|
|
113
|
+
git commit -m "feat!: change API response format
|
|
114
|
+
|
|
115
|
+
BREAKING CHANGE: API responses now use 'data' wrapper"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Branch Management
|
|
119
|
+
|
|
120
|
+
### Git Flow Strategy
|
|
121
|
+
```bash
|
|
122
|
+
# Main branches
|
|
123
|
+
main # Production-ready code
|
|
124
|
+
develop # Integration branch
|
|
125
|
+
|
|
126
|
+
# Supporting branches
|
|
127
|
+
feature/* # New features
|
|
128
|
+
release/* # Release preparation
|
|
129
|
+
hotfix/* # Quick fixes to production
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### GitHub Flow (Simplified)
|
|
133
|
+
```bash
|
|
134
|
+
# Only main branch + feature branches
|
|
135
|
+
main # Production-ready code
|
|
136
|
+
feature/* # All new work
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Conflict Resolution
|
|
140
|
+
|
|
141
|
+
### When Conflicts Occur
|
|
142
|
+
```bash
|
|
143
|
+
# Start merge/rebase
|
|
144
|
+
git merge feature-branch
|
|
145
|
+
# OR
|
|
146
|
+
git rebase main
|
|
147
|
+
|
|
148
|
+
# If conflicts occur, Git will list conflicted files
|
|
149
|
+
# Edit each file to resolve conflicts
|
|
150
|
+
|
|
151
|
+
# Mark conflicts as resolved
|
|
152
|
+
git add conflicted-file.js
|
|
153
|
+
|
|
154
|
+
# Continue the merge/rebase
|
|
155
|
+
git merge --continue
|
|
156
|
+
# OR
|
|
157
|
+
git rebase --continue
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Conflict Markers
|
|
161
|
+
```javascript
|
|
162
|
+
<<<<<<< HEAD
|
|
163
|
+
// Your current branch code
|
|
164
|
+
const user = getCurrentUser();
|
|
165
|
+
=======
|
|
166
|
+
// Incoming branch code
|
|
167
|
+
const user = getAuthenticatedUser();
|
|
168
|
+
>>>>>>> feature-branch
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Useful Git Commands
|
|
172
|
+
|
|
173
|
+
### Status and Information
|
|
174
|
+
```bash
|
|
175
|
+
git status # Check working directory status
|
|
176
|
+
git log --oneline # View commit history
|
|
177
|
+
git branch -a # List all branches
|
|
178
|
+
git remote -v # List remote repositories
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Undoing Changes
|
|
182
|
+
```bash
|
|
183
|
+
git checkout -- file.js # Discard changes to file
|
|
184
|
+
git reset HEAD file.js # Unstage file
|
|
185
|
+
git reset --soft HEAD~1 # Undo last commit (keep changes)
|
|
186
|
+
git reset --hard HEAD~1 # Undo last commit (discard changes)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Stashing Work
|
|
190
|
+
```bash
|
|
191
|
+
git stash # Save current work
|
|
192
|
+
git stash pop # Apply and remove latest stash
|
|
193
|
+
git stash list # List all stashes
|
|
194
|
+
git stash apply stash@{1} # Apply specific stash
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Best Practices
|
|
198
|
+
|
|
199
|
+
1. **Commit Often** - Make small, focused commits
|
|
200
|
+
2. **Write Clear Messages** - Use conventional commit format
|
|
201
|
+
3. **Test Before Committing** - Ensure code works
|
|
202
|
+
4. **Pull Before Push** - Keep history clean
|
|
203
|
+
5. **Use Branches** - Don't work directly on main
|
|
204
|
+
6. **Review Code** - Use pull requests for collaboration
|
|
205
|
+
7. **Keep History Clean** - Rebase feature branches when appropriate
|
|
206
|
+
|
|
207
|
+
## Git Hooks (Optional)
|
|
208
|
+
|
|
209
|
+
### Pre-commit Hook
|
|
210
|
+
```bash
|
|
211
|
+
#!/bin/sh
|
|
212
|
+
# .git/hooks/pre-commit
|
|
213
|
+
|
|
214
|
+
# Run linter
|
|
215
|
+
npm run lint
|
|
216
|
+
if [ $? -ne 0 ]; then
|
|
217
|
+
echo "Linting failed. Please fix errors before committing."
|
|
218
|
+
exit 1
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
# Run tests
|
|
222
|
+
npm test
|
|
223
|
+
if [ $? -ne 0 ]; then
|
|
224
|
+
echo "Tests failed. Please fix tests before committing."
|
|
225
|
+
exit 1
|
|
226
|
+
fi
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Commit Message Hook
|
|
230
|
+
```bash
|
|
231
|
+
#!/bin/sh
|
|
232
|
+
# .git/hooks/commit-msg
|
|
233
|
+
|
|
234
|
+
# Check commit message format
|
|
235
|
+
if ! grep -qE "^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,50}" "$1"; then
|
|
236
|
+
echo "Invalid commit message format. Use conventional commits."
|
|
237
|
+
exit 1
|
|
238
|
+
fi
|
|
239
|
+
```
|