feat(git-workflow): Simplify git integration with --from-branch option
- Remove automatic git workflow and branch-tag switching - we are not ready for it yet - Add --from-branch option to add-tag command for manual tag creation from git branch - Remove git workflow configuration from config.json and assets - Disable automatic tag switching functions in git-utils.js - Add createTagFromBranch function for branch-based tag creation - Support both CLI and MCP interfaces for --from-branch functionality - Fix ES module imports in git-utils.js and utils.js - Maintain user control over tag contexts without forced automation The simplified approach allows users to create tags from their current git branch when desired, without the complexity and rigidity of automatic branch-tag synchronization. Users maintain full control over their tag contexts while having convenient tools for git-based workflows when needed.
This commit is contained in:
@@ -1,13 +0,0 @@
|
|||||||
---
|
|
||||||
"task-master-ai": minor
|
|
||||||
---
|
|
||||||
|
|
||||||
Add an experimental automatic git branch-tag integration for seamless multi-context development
|
|
||||||
|
|
||||||
- **Automatic Tag Creation**: System now automatically creates empty tags when switching to new git branches
|
|
||||||
- **Branch-Tag Mapping**: Maintains mapping between git branches and task contexts for seamless workflow
|
|
||||||
- **Auto-Switch on Branch Change**: Task context automatically switches when you change git branches (when git workflow is enabled)
|
|
||||||
- **Isolated Task Contexts**: Each branch gets its own clean task context, preventing merge conflicts and enabling parallel development
|
|
||||||
- **Configuration Support**: Git workflow features can be enabled/disabled via `.taskmaster/config.json`
|
|
||||||
- **Zero Migration Impact**: Existing projects continue working unchanged with automatic migration to "master" tag
|
|
||||||
- **ES Module Compatibility**: Fixed git-utils module to work properly with ES module architecture
|
|
||||||
@@ -38,6 +38,7 @@ Task Master's multi-context capabilities are now fully exposed through the MCP s
|
|||||||
**Tag Creation Options:**
|
**Tag Creation Options:**
|
||||||
- `--copy-from-current` - Copy tasks from currently active tag
|
- `--copy-from-current` - Copy tasks from currently active tag
|
||||||
- `--copy-from=<tag>` - Copy tasks from specific tag
|
- `--copy-from=<tag>` - Copy tasks from specific tag
|
||||||
|
- `--from-branch` - Creates a new tag usin active git branch name (for `add-tag` only)
|
||||||
- `--description="<text>"` - Add custom tag descriptions
|
- `--description="<text>"` - Add custom tag descriptions
|
||||||
- Empty tag creation for fresh contexts
|
- Empty tag creation for fresh contexts
|
||||||
|
|
||||||
|
|||||||
@@ -30,9 +30,5 @@
|
|||||||
"bedrockBaseURL": "https://bedrock.us-east-1.amazonaws.com",
|
"bedrockBaseURL": "https://bedrock.us-east-1.amazonaws.com",
|
||||||
"azureBaseURL": "https://your-endpoint.azure.com/",
|
"azureBaseURL": "https://your-endpoint.azure.com/",
|
||||||
"defaultTag": "master"
|
"defaultTag": "master"
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"enabledGitworkflow": true,
|
|
||||||
"autoSwitchTagWithBranch": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"currentTag": "v017-adds",
|
"currentTag": "master",
|
||||||
"lastSwitched": "2025-06-13T22:24:34.119Z",
|
"lastSwitched": "2025-06-13T22:44:52.009Z",
|
||||||
"branchTagMapping": {
|
"branchTagMapping": {
|
||||||
"v017-adds": "v017-adds"
|
"v017-adds": "v017-adds"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -29,12 +29,5 @@
|
|||||||
"ollamaBaseURL": "http://localhost:11434/api",
|
"ollamaBaseURL": "http://localhost:11434/api",
|
||||||
"azureOpenaiBaseURL": "https://your-endpoint.openai.azure.com/",
|
"azureOpenaiBaseURL": "https://your-endpoint.openai.azure.com/",
|
||||||
"bedrockBaseURL": "https://bedrock.us-east-1.amazonaws.com"
|
"bedrockBaseURL": "https://bedrock.us-east-1.amazonaws.com"
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"autoSwitchOnBranch": false,
|
|
||||||
"gitIntegration": {
|
|
||||||
"enabled": false,
|
|
||||||
"autoSwitchTagWithBranch": false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
* Direct function implementation for creating a new tag
|
* Direct function implementation for creating a new tag
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createTag } from '../../../../scripts/modules/task-manager/tag-management.js';
|
import {
|
||||||
|
createTag,
|
||||||
|
createTagFromBranch
|
||||||
|
} from '../../../../scripts/modules/task-manager/tag-management.js';
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
@@ -17,6 +20,7 @@ import { createLogWrapper } from '../../tools/utils.js';
|
|||||||
* @param {string} args.name - Name of the new tag to create
|
* @param {string} args.name - Name of the new tag to create
|
||||||
* @param {boolean} [args.copyFromCurrent=false] - Whether to copy tasks from current tag
|
* @param {boolean} [args.copyFromCurrent=false] - Whether to copy tasks from current tag
|
||||||
* @param {string} [args.copyFromTag] - Specific tag to copy tasks from
|
* @param {string} [args.copyFromTag] - Specific tag to copy tasks from
|
||||||
|
* @param {boolean} [args.fromBranch=false] - Create tag name from current git branch
|
||||||
* @param {string} [args.description] - Optional description for the tag
|
* @param {string} [args.description] - Optional description for the tag
|
||||||
* @param {string} [args.tasksJsonPath] - Path to the tasks.json file (resolved by tool)
|
* @param {string} [args.tasksJsonPath] - Path to the tasks.json file (resolved by tool)
|
||||||
* @param {string} [args.projectRoot] - Project root path
|
* @param {string} [args.projectRoot] - Project root path
|
||||||
@@ -31,6 +35,7 @@ export async function addTagDirect(args, log, context = {}) {
|
|||||||
name,
|
name,
|
||||||
copyFromCurrent = false,
|
copyFromCurrent = false,
|
||||||
copyFromTag,
|
copyFromTag,
|
||||||
|
fromBranch = false,
|
||||||
description,
|
description,
|
||||||
projectRoot
|
projectRoot
|
||||||
} = args;
|
} = args;
|
||||||
@@ -56,55 +61,127 @@ export async function addTagDirect(args, log, context = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check required parameters
|
// Handle --from-branch option
|
||||||
if (!name || typeof name !== 'string') {
|
if (fromBranch) {
|
||||||
log.error('Missing required parameter: name');
|
log.info('Creating tag from current git branch');
|
||||||
|
|
||||||
|
// Import git utilities
|
||||||
|
const gitUtils = await import(
|
||||||
|
'../../../../scripts/modules/utils/git-utils.js'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if we're in a git repository
|
||||||
|
if (!(await gitUtils.isGitRepository(projectRoot))) {
|
||||||
|
log.error('Not in a git repository');
|
||||||
|
disableSilentMode();
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: 'NOT_GIT_REPO',
|
||||||
|
message: 'Not in a git repository. Cannot use fromBranch option.'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current git branch
|
||||||
|
const currentBranch = await gitUtils.getCurrentBranch(projectRoot);
|
||||||
|
if (!currentBranch) {
|
||||||
|
log.error('Could not determine current git branch');
|
||||||
|
disableSilentMode();
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: 'NO_CURRENT_BRANCH',
|
||||||
|
message: 'Could not determine current git branch.'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare options for branch-based tag creation
|
||||||
|
const branchOptions = {
|
||||||
|
copyFromCurrent,
|
||||||
|
copyFromTag,
|
||||||
|
description:
|
||||||
|
description || `Tag created from git branch "${currentBranch}"`
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call the createTagFromBranch function
|
||||||
|
const result = await createTagFromBranch(
|
||||||
|
tasksJsonPath,
|
||||||
|
currentBranch,
|
||||||
|
branchOptions,
|
||||||
|
{
|
||||||
|
session,
|
||||||
|
mcpLog,
|
||||||
|
projectRoot
|
||||||
|
},
|
||||||
|
'json' // outputFormat - use 'json' to suppress CLI UI
|
||||||
|
);
|
||||||
|
|
||||||
|
// Restore normal logging
|
||||||
disableSilentMode();
|
disableSilentMode();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: true,
|
||||||
error: {
|
data: {
|
||||||
code: 'MISSING_PARAMETER',
|
branchName: result.branchName,
|
||||||
message: 'Tag name is required and must be a string'
|
tagName: result.tagName,
|
||||||
|
created: result.created,
|
||||||
|
mappingUpdated: result.mappingUpdated,
|
||||||
|
message: `Successfully created tag "${result.tagName}" from git branch "${result.branchName}"`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Check required parameters for regular tag creation
|
||||||
|
if (!name || typeof name !== 'string') {
|
||||||
|
log.error('Missing required parameter: name');
|
||||||
|
disableSilentMode();
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: 'MISSING_PARAMETER',
|
||||||
|
message: 'Tag name is required and must be a string'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info(`Creating new tag: ${name}`);
|
||||||
|
|
||||||
|
// Prepare options
|
||||||
|
const options = {
|
||||||
|
copyFromCurrent,
|
||||||
|
copyFromTag,
|
||||||
|
description
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call the createTag function
|
||||||
|
const result = await createTag(
|
||||||
|
tasksJsonPath,
|
||||||
|
name,
|
||||||
|
options,
|
||||||
|
{
|
||||||
|
session,
|
||||||
|
mcpLog,
|
||||||
|
projectRoot
|
||||||
|
},
|
||||||
|
'json' // outputFormat - use 'json' to suppress CLI UI
|
||||||
|
);
|
||||||
|
|
||||||
|
// Restore normal logging
|
||||||
|
disableSilentMode();
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
tagName: result.tagName,
|
||||||
|
created: result.created,
|
||||||
|
tasksCopied: result.tasksCopied,
|
||||||
|
sourceTag: result.sourceTag,
|
||||||
|
description: result.description,
|
||||||
|
message: `Successfully created tag "${result.tagName}"`
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`Creating new tag: ${name}`);
|
|
||||||
|
|
||||||
// Prepare options
|
|
||||||
const options = {
|
|
||||||
copyFromCurrent,
|
|
||||||
copyFromTag,
|
|
||||||
description
|
|
||||||
};
|
|
||||||
|
|
||||||
// Call the createTag function
|
|
||||||
const result = await createTag(
|
|
||||||
tasksJsonPath,
|
|
||||||
name,
|
|
||||||
options,
|
|
||||||
{
|
|
||||||
session,
|
|
||||||
mcpLog,
|
|
||||||
projectRoot
|
|
||||||
},
|
|
||||||
'json' // outputFormat - use 'json' to suppress CLI UI
|
|
||||||
);
|
|
||||||
|
|
||||||
// Restore normal logging
|
|
||||||
disableSilentMode();
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
data: {
|
|
||||||
tagName: result.tagName,
|
|
||||||
created: result.created,
|
|
||||||
tasksCopied: result.tasksCopied,
|
|
||||||
sourceTag: result.sourceTag,
|
|
||||||
description: result.description,
|
|
||||||
message: `Successfully created tag "${result.tagName}"`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Make sure to restore normal logging even if there's an error
|
// Make sure to restore normal logging even if there's an error
|
||||||
disableSilentMode();
|
disableSilentMode();
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ export function registerAddTagTool(server) {
|
|||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.describe('Specific tag to copy tasks from'),
|
.describe('Specific tag to copy tasks from'),
|
||||||
|
fromBranch: z
|
||||||
|
.boolean()
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
'Create tag name from current git branch (ignores name parameter)'
|
||||||
|
),
|
||||||
description: z
|
description: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
@@ -69,6 +75,7 @@ export function registerAddTagTool(server) {
|
|||||||
name: args.name,
|
name: args.name,
|
||||||
copyFromCurrent: args.copyFromCurrent,
|
copyFromCurrent: args.copyFromCurrent,
|
||||||
copyFromTag: args.copyFromTag,
|
copyFromTag: args.copyFromTag,
|
||||||
|
fromBranch: args.fromBranch,
|
||||||
description: args.description,
|
description: args.description,
|
||||||
projectRoot: args.projectRoot
|
projectRoot: args.projectRoot
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3715,6 +3715,10 @@ Examples:
|
|||||||
'--copy-from <tag>',
|
'--copy-from <tag>',
|
||||||
'Copy tasks from the specified tag to the new tag'
|
'Copy tasks from the specified tag to the new tag'
|
||||||
)
|
)
|
||||||
|
.option(
|
||||||
|
'--from-branch',
|
||||||
|
'Create tag name from current git branch (ignores tagName argument)'
|
||||||
|
)
|
||||||
.option('-d, --description <text>', 'Optional description for the tag')
|
.option('-d, --description <text>', 'Optional description for the tag')
|
||||||
.action(async (tagName, options) => {
|
.action(async (tagName, options) => {
|
||||||
try {
|
try {
|
||||||
@@ -3745,19 +3749,70 @@ Examples:
|
|||||||
outputType: 'cli'
|
outputType: 'cli'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Regular tag creation
|
// Handle --from-branch option
|
||||||
const createOptions = {
|
if (options.fromBranch) {
|
||||||
copyFromCurrent: options.copyFromCurrent || false,
|
const { createTagFromBranch } = await import(
|
||||||
copyFromTag: options.copyFrom,
|
'./task-manager/tag-management.js'
|
||||||
description: options.description
|
);
|
||||||
};
|
const gitUtils = await import('./utils/git-utils.js');
|
||||||
|
|
||||||
await createTag(tasksPath, tagName, createOptions, context, 'text');
|
// Check if we're in a git repository
|
||||||
|
if (!(await gitUtils.isGitRepository(projectRoot))) {
|
||||||
|
console.error(
|
||||||
|
chalk.red(
|
||||||
|
'Error: Not in a git repository. Cannot use --from-branch option.'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current git branch
|
||||||
|
const currentBranch = await gitUtils.getCurrentBranch(projectRoot);
|
||||||
|
if (!currentBranch) {
|
||||||
|
console.error(
|
||||||
|
chalk.red('Error: Could not determine current git branch.')
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create tag from branch
|
||||||
|
const branchOptions = {
|
||||||
|
copyFromCurrent: options.copyFromCurrent || false,
|
||||||
|
copyFromTag: options.copyFrom,
|
||||||
|
description:
|
||||||
|
options.description ||
|
||||||
|
`Tag created from git branch "${currentBranch}"`
|
||||||
|
};
|
||||||
|
|
||||||
|
await createTagFromBranch(
|
||||||
|
tasksPath,
|
||||||
|
currentBranch,
|
||||||
|
branchOptions,
|
||||||
|
context,
|
||||||
|
'text'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Regular tag creation
|
||||||
|
const createOptions = {
|
||||||
|
copyFromCurrent: options.copyFromCurrent || false,
|
||||||
|
copyFromTag: options.copyFrom,
|
||||||
|
description: options.description
|
||||||
|
};
|
||||||
|
|
||||||
|
await createTag(tasksPath, tagName, createOptions, context, 'text');
|
||||||
|
}
|
||||||
|
|
||||||
// Handle auto-switch if requested
|
// Handle auto-switch if requested
|
||||||
if (options.autoSwitch) {
|
if (options.autoSwitch) {
|
||||||
const { useTag } = await import('./task-manager/tag-management.js');
|
const { useTag } = await import('./task-manager/tag-management.js');
|
||||||
await useTag(tasksPath, tagName, {}, context, 'text');
|
const finalTagName = options.fromBranch
|
||||||
|
? (await import('./utils/git-utils.js')).sanitizeBranchNameForTag(
|
||||||
|
await (await import('./utils/git-utils.js')).getCurrentBranch(
|
||||||
|
projectRoot
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: tagName;
|
||||||
|
await useTag(tasksPath, finalTagName, {}, context, 'text');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(chalk.red(`Error creating tag: ${error.message}`));
|
console.error(chalk.red(`Error creating tag: ${error.message}`));
|
||||||
|
|||||||
@@ -1235,7 +1235,7 @@ async function createTagFromBranch(
|
|||||||
|
|
||||||
// Import git utilities
|
// Import git utilities
|
||||||
const { sanitizeBranchNameForTag, isValidBranchForTag } = await import(
|
const { sanitizeBranchNameForTag, isValidBranchForTag } = await import(
|
||||||
'../../utils/git-utils.js'
|
'../utils/git-utils.js'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create a consistent logFn object regardless of context
|
// Create a consistent logFn object regardless of context
|
||||||
@@ -1341,7 +1341,7 @@ async function autoSwitchTagForBranch(
|
|||||||
isGitRepository,
|
isGitRepository,
|
||||||
sanitizeBranchNameForTag,
|
sanitizeBranchNameForTag,
|
||||||
isValidBranchForTag
|
isValidBranchForTag
|
||||||
} = await import('../../utils/git-utils.js');
|
} = await import('../utils/git-utils.js');
|
||||||
|
|
||||||
// Create a consistent logFn object regardless of context
|
// Create a consistent logFn object regardless of context
|
||||||
const logFn = mcpLog || {
|
const logFn = mcpLog || {
|
||||||
@@ -1475,13 +1475,8 @@ async function checkAndAutoSwitchTag(projectRoot, tasksPath, context = {}) {
|
|||||||
const rawConfig = fs.readFileSync(configPath, 'utf8');
|
const rawConfig = fs.readFileSync(configPath, 'utf8');
|
||||||
const config = JSON.parse(rawConfig);
|
const config = JSON.parse(rawConfig);
|
||||||
|
|
||||||
// Check if git workflow is enabled
|
// Git workflow has been removed - return null to disable auto-switching
|
||||||
const gitWorkflowEnabled = config.tags?.enabledGitworkflow || false;
|
return null;
|
||||||
const autoSwitchEnabled = config.tags?.autoSwitchTagWithBranch || false;
|
|
||||||
|
|
||||||
if (!gitWorkflowEnabled || !autoSwitchEnabled) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform auto-switch
|
// Perform auto-switch
|
||||||
return await autoSwitchTagForBranch(
|
return await autoSwitchTagForBranch(
|
||||||
|
|||||||
@@ -524,15 +524,6 @@ function migrateConfigJson(configPath) {
|
|||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add tags section if missing
|
|
||||||
if (!config.tags) {
|
|
||||||
config.tags = {
|
|
||||||
enabledGitworkflow: false,
|
|
||||||
autoSwitchTagWithBranch: false
|
|
||||||
};
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modified) {
|
if (modified) {
|
||||||
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
|
||||||
if (process.env.TASKMASTER_DEBUG === 'true') {
|
if (process.env.TASKMASTER_DEBUG === 'true') {
|
||||||
|
|||||||
@@ -285,134 +285,9 @@ async function checkAndAutoSwitchGitTag(projectRoot, tasksPath) {
|
|||||||
throw new Error('projectRoot is required for checkAndAutoSwitchGitTag');
|
throw new Error('projectRoot is required for checkAndAutoSwitchGitTag');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
// DISABLED: Automatic git workflow is too rigid and opinionated
|
||||||
// Only proceed if we have a valid project root
|
// Users should explicitly use git-tag commands if they want integration
|
||||||
if (!fs.existsSync(projectRoot)) {
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read configuration to check if git workflow is enabled
|
|
||||||
const configPath = path.join(projectRoot, '.taskmaster', 'config.json');
|
|
||||||
if (!fs.existsSync(configPath)) {
|
|
||||||
return; // No config, git workflow disabled
|
|
||||||
}
|
|
||||||
|
|
||||||
const rawConfig = fs.readFileSync(configPath, 'utf8');
|
|
||||||
const config = JSON.parse(rawConfig);
|
|
||||||
|
|
||||||
// Check if git workflow features are enabled
|
|
||||||
const gitWorkflowEnabled = config.tags?.enabledGitworkflow || false;
|
|
||||||
const autoSwitchEnabled = config.tags?.autoSwitchTagWithBranch || false;
|
|
||||||
|
|
||||||
if (!gitWorkflowEnabled || !autoSwitchEnabled) {
|
|
||||||
return; // Git integration disabled
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we're in a git repository
|
|
||||||
if (!(await isGitRepository(projectRoot))) {
|
|
||||||
return; // Not a git repo
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current git branch
|
|
||||||
const currentBranch = await getCurrentBranch(projectRoot);
|
|
||||||
if (!currentBranch || !isValidBranchForTag(currentBranch)) {
|
|
||||||
return; // No valid branch for tag creation
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine expected tag name from branch
|
|
||||||
const expectedTag = sanitizeBranchNameForTag(currentBranch);
|
|
||||||
|
|
||||||
// Get current tag from state.json
|
|
||||||
const statePath = path.join(projectRoot, '.taskmaster', 'state.json');
|
|
||||||
let currentTag = 'master'; // Default fallback
|
|
||||||
if (fs.existsSync(statePath)) {
|
|
||||||
try {
|
|
||||||
const rawState = fs.readFileSync(statePath, 'utf8');
|
|
||||||
const state = JSON.parse(rawState);
|
|
||||||
currentTag = state.currentTag || 'master';
|
|
||||||
} catch (error) {
|
|
||||||
// Use default if state reading fails
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're already on the correct tag, nothing to do
|
|
||||||
if (currentTag === expectedTag) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the expected tag exists by reading tasks.json
|
|
||||||
if (!fs.existsSync(tasksPath)) {
|
|
||||||
return; // No tasks file to work with
|
|
||||||
}
|
|
||||||
|
|
||||||
const rawTasksData = fs.readFileSync(tasksPath, 'utf8');
|
|
||||||
const tasksData = JSON.parse(rawTasksData);
|
|
||||||
const tagExists = tasksData[expectedTag];
|
|
||||||
|
|
||||||
if (tagExists) {
|
|
||||||
// Tag exists, switch to it
|
|
||||||
console.log(
|
|
||||||
`Auto-switching to tag "${expectedTag}" for branch "${currentBranch}"`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update current tag in state.json
|
|
||||||
let state = {};
|
|
||||||
if (fs.existsSync(statePath)) {
|
|
||||||
const rawState = fs.readFileSync(statePath, 'utf8');
|
|
||||||
state = JSON.parse(rawState);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.currentTag = expectedTag;
|
|
||||||
state.lastSwitched = new Date().toISOString();
|
|
||||||
|
|
||||||
// Ensure branchTagMapping exists and update it
|
|
||||||
if (!state.branchTagMapping) {
|
|
||||||
state.branchTagMapping = {};
|
|
||||||
}
|
|
||||||
state.branchTagMapping[currentBranch] = expectedTag;
|
|
||||||
|
|
||||||
fs.writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf8');
|
|
||||||
} else {
|
|
||||||
// Tag doesn't exist, create it automatically
|
|
||||||
console.log(
|
|
||||||
`Auto-creating tag "${expectedTag}" for branch "${currentBranch}"`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create the tag with a descriptive name
|
|
||||||
const newTagData = {
|
|
||||||
tasks: [],
|
|
||||||
metadata: {
|
|
||||||
created: new Date().toISOString(),
|
|
||||||
updated: new Date().toISOString(),
|
|
||||||
description: `Automatically created from git branch "${currentBranch}"`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add the new tag to the tasks data
|
|
||||||
tasksData[expectedTag] = newTagData;
|
|
||||||
fs.writeFileSync(tasksPath, JSON.stringify(tasksData, null, 2), 'utf8');
|
|
||||||
|
|
||||||
// Update branch-tag mapping
|
|
||||||
let state = {};
|
|
||||||
if (fs.existsSync(statePath)) {
|
|
||||||
const rawState = fs.readFileSync(statePath, 'utf8');
|
|
||||||
state = JSON.parse(rawState);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.currentTag = expectedTag;
|
|
||||||
state.lastSwitched = new Date().toISOString();
|
|
||||||
|
|
||||||
if (!state.branchTagMapping) {
|
|
||||||
state.branchTagMapping = {};
|
|
||||||
}
|
|
||||||
state.branchTagMapping[currentBranch] = expectedTag;
|
|
||||||
|
|
||||||
fs.writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf8');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// Silently fail - don't break normal operations
|
|
||||||
console.debug(`Git auto-switch failed: ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -427,148 +302,9 @@ function checkAndAutoSwitchGitTagSync(projectRoot, tasksPath) {
|
|||||||
return; // Can't proceed without project root
|
return; // Can't proceed without project root
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
// DISABLED: Automatic git workflow is too rigid and opinionated
|
||||||
// Only proceed if we have a valid project root
|
// Users should explicitly use git-tag commands if they want integration
|
||||||
if (!fs.existsSync(projectRoot)) {
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read configuration to check if git workflow is enabled
|
|
||||||
const configPath = path.join(projectRoot, '.taskmaster', 'config.json');
|
|
||||||
if (!fs.existsSync(configPath)) {
|
|
||||||
return; // No config, git workflow disabled
|
|
||||||
}
|
|
||||||
|
|
||||||
const rawConfig = fs.readFileSync(configPath, 'utf8');
|
|
||||||
const config = JSON.parse(rawConfig);
|
|
||||||
|
|
||||||
// Check if git workflow features are enabled
|
|
||||||
const gitWorkflowEnabled = config.tags?.enabledGitworkflow || false;
|
|
||||||
const autoSwitchEnabled = config.tags?.autoSwitchTagWithBranch || false;
|
|
||||||
|
|
||||||
if (!gitWorkflowEnabled || !autoSwitchEnabled) {
|
|
||||||
return; // Git integration disabled
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we're in a git repository (synchronously)
|
|
||||||
if (!isGitRepositorySync(projectRoot)) {
|
|
||||||
return; // Not a git repo
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current git branch (synchronously)
|
|
||||||
const currentBranch = getCurrentBranchSync(projectRoot);
|
|
||||||
if (!currentBranch || !isValidBranchForTag(currentBranch)) {
|
|
||||||
return; // No valid branch for tag creation
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine expected tag name from branch
|
|
||||||
const expectedTag = sanitizeBranchNameForTag(currentBranch);
|
|
||||||
|
|
||||||
// Get current tag from state.json
|
|
||||||
const statePath = path.join(projectRoot, '.taskmaster', 'state.json');
|
|
||||||
let currentTag = 'master'; // default
|
|
||||||
if (fs.existsSync(statePath)) {
|
|
||||||
try {
|
|
||||||
const rawState = fs.readFileSync(statePath, 'utf8');
|
|
||||||
const state = JSON.parse(rawState);
|
|
||||||
currentTag = state.currentTag || 'master';
|
|
||||||
} catch (error) {
|
|
||||||
// Use default if state.json is corrupted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're already on the correct tag, nothing to do
|
|
||||||
if (currentTag === expectedTag) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the expected tag exists in tasks.json
|
|
||||||
let tasksData = {};
|
|
||||||
if (fs.existsSync(tasksPath)) {
|
|
||||||
try {
|
|
||||||
const rawTasks = fs.readFileSync(tasksPath, 'utf8');
|
|
||||||
tasksData = JSON.parse(rawTasks);
|
|
||||||
} catch (error) {
|
|
||||||
return; // Can't read tasks file
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const tagExists = tasksData[expectedTag];
|
|
||||||
|
|
||||||
if (tagExists) {
|
|
||||||
// Tag exists, switch to it
|
|
||||||
console.log(
|
|
||||||
`Auto-switching to tag "${expectedTag}" for branch "${currentBranch}"`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update current tag in state.json
|
|
||||||
let state = {};
|
|
||||||
if (fs.existsSync(statePath)) {
|
|
||||||
try {
|
|
||||||
const rawState = fs.readFileSync(statePath, 'utf8');
|
|
||||||
state = JSON.parse(rawState);
|
|
||||||
} catch (error) {
|
|
||||||
state = {}; // Start fresh if corrupted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state.currentTag = expectedTag;
|
|
||||||
state.lastSwitched = new Date().toISOString();
|
|
||||||
|
|
||||||
// Ensure branchTagMapping exists and update it
|
|
||||||
if (!state.branchTagMapping) {
|
|
||||||
state.branchTagMapping = {};
|
|
||||||
}
|
|
||||||
state.branchTagMapping[currentBranch] = expectedTag;
|
|
||||||
|
|
||||||
fs.writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf8');
|
|
||||||
} else {
|
|
||||||
// Tag doesn't exist, create it automatically
|
|
||||||
console.log(
|
|
||||||
`Auto-creating tag "${expectedTag}" for branch "${currentBranch}"`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create the tag with a descriptive name
|
|
||||||
const newTagData = {
|
|
||||||
tasks: [],
|
|
||||||
metadata: {
|
|
||||||
created: new Date().toISOString(),
|
|
||||||
updated: new Date().toISOString(),
|
|
||||||
description: `Automatically created from git branch "${currentBranch}"`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add the new tag to the tasks data
|
|
||||||
tasksData[expectedTag] = newTagData;
|
|
||||||
fs.writeFileSync(tasksPath, JSON.stringify(tasksData, null, 2), 'utf8');
|
|
||||||
|
|
||||||
// Update branch-tag mapping and current tag
|
|
||||||
let state = {};
|
|
||||||
if (fs.existsSync(statePath)) {
|
|
||||||
try {
|
|
||||||
const rawState = fs.readFileSync(statePath, 'utf8');
|
|
||||||
state = JSON.parse(rawState);
|
|
||||||
} catch (error) {
|
|
||||||
state = {}; // Start fresh if corrupted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state.currentTag = expectedTag;
|
|
||||||
state.lastSwitched = new Date().toISOString();
|
|
||||||
|
|
||||||
if (!state.branchTagMapping) {
|
|
||||||
state.branchTagMapping = {};
|
|
||||||
}
|
|
||||||
state.branchTagMapping[currentBranch] = expectedTag;
|
|
||||||
|
|
||||||
fs.writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf8');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// Silently fail - don't break normal operations
|
|
||||||
if (process.env.TASKMASTER_DEBUG === 'true') {
|
|
||||||
console.debug(`Git auto-switch failed: ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user