├── images ├── gitflow.jpg ├── HotfixName.png ├── TagHotfix.png ├── TagRelease.png ├── DevelopBranch.png ├── FeatureBranch.png ├── FeatureName.png ├── FinishFeature.png ├── FinishHotfix.png ├── FinishRelease.png ├── HotfixBranch.png ├── MasterBranch.png ├── ReleaseBranch.png ├── ReleaseName.png ├── Hotfix-Command.png ├── Release-Command.png ├── StartHotfixBase.png ├── GetStatus-Command.png ├── GetStatus-Result.png ├── Initialize-Command.png ├── InitializeCustom.png ├── InitializeSettings.png ├── StartFeatureBase.png ├── StartHotfixMaster.png ├── StartReleaseBase.png ├── DeletionConfigValues.png ├── StartFeature-Command.png ├── StartFeatureDevelop.png ├── StartHotfix-Command.png ├── StartRelease-Command.png ├── StartReleaseDevelop.png ├── FinishFeature-Statusbar.png ├── FinishHotfix-Statusbar.png ├── FinishRelease-Statusbar.png └── InitializeWithDefault.png ├── gitflow4code-0.0.1.vsix ├── gitflow4code-0.0.2.vsix ├── gitflow4code-0.0.3.vsix ├── gitflow4code-0.0.4.vsix ├── gitflow4code-0.0.5.vsix ├── gitflow4code-0.1.0.vsix ├── gitflow4code-0.1.1.vsix ├── gitflow4code-1.0.0.vsix ├── gitflow4code-1.1.0.vsix ├── gitflow4code-1.2.0.vsix ├── gitflow4code-1.2.1.vsix ├── gitflow4code-1.2.2.vsix ├── gitflow4code-1.2.3.vsix ├── .vscodeignore ├── tsconfig.json ├── src ├── settings │ ├── branchSettings.ts │ └── configSettings.ts ├── util.ts ├── view.ts ├── commands │ ├── gitCommands.ts │ ├── init.ts │ ├── hotfixes.ts │ ├── releases.ts │ └── features.ts ├── helpers │ ├── gitUtils.ts │ └── gitflowUtils.ts └── gitflow.ts ├── .vscode ├── tasks.json └── launch.json ├── test ├── features.test.ts └── index.ts ├── LICENSE ├── .gitignore ├── vsc-extension-quickstart.md ├── CHANGELOG.md ├── README.md └── package.json /images/gitflow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/gitflow.jpg -------------------------------------------------------------------------------- /images/HotfixName.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/HotfixName.png -------------------------------------------------------------------------------- /images/TagHotfix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/TagHotfix.png -------------------------------------------------------------------------------- /images/TagRelease.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/TagRelease.png -------------------------------------------------------------------------------- /gitflow4code-0.0.1.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/gitflow4code-0.0.1.vsix -------------------------------------------------------------------------------- /gitflow4code-0.0.2.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/gitflow4code-0.0.2.vsix -------------------------------------------------------------------------------- /gitflow4code-0.0.3.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/gitflow4code-0.0.3.vsix -------------------------------------------------------------------------------- /gitflow4code-0.0.4.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/gitflow4code-0.0.4.vsix -------------------------------------------------------------------------------- /gitflow4code-0.0.5.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/gitflow4code-0.0.5.vsix -------------------------------------------------------------------------------- /gitflow4code-0.1.0.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/gitflow4code-0.1.0.vsix -------------------------------------------------------------------------------- /gitflow4code-0.1.1.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/gitflow4code-0.1.1.vsix -------------------------------------------------------------------------------- /gitflow4code-1.0.0.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/gitflow4code-1.0.0.vsix -------------------------------------------------------------------------------- /gitflow4code-1.1.0.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/gitflow4code-1.1.0.vsix -------------------------------------------------------------------------------- /gitflow4code-1.2.0.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/gitflow4code-1.2.0.vsix -------------------------------------------------------------------------------- /gitflow4code-1.2.1.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/gitflow4code-1.2.1.vsix -------------------------------------------------------------------------------- /gitflow4code-1.2.2.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/gitflow4code-1.2.2.vsix -------------------------------------------------------------------------------- /gitflow4code-1.2.3.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/gitflow4code-1.2.3.vsix -------------------------------------------------------------------------------- /images/DevelopBranch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/DevelopBranch.png -------------------------------------------------------------------------------- /images/FeatureBranch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/FeatureBranch.png -------------------------------------------------------------------------------- /images/FeatureName.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/FeatureName.png -------------------------------------------------------------------------------- /images/FinishFeature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/FinishFeature.png -------------------------------------------------------------------------------- /images/FinishHotfix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/FinishHotfix.png -------------------------------------------------------------------------------- /images/FinishRelease.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/FinishRelease.png -------------------------------------------------------------------------------- /images/HotfixBranch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/HotfixBranch.png -------------------------------------------------------------------------------- /images/MasterBranch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/MasterBranch.png -------------------------------------------------------------------------------- /images/ReleaseBranch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/ReleaseBranch.png -------------------------------------------------------------------------------- /images/ReleaseName.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/ReleaseName.png -------------------------------------------------------------------------------- /images/Hotfix-Command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/Hotfix-Command.png -------------------------------------------------------------------------------- /images/Release-Command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/Release-Command.png -------------------------------------------------------------------------------- /images/StartHotfixBase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/StartHotfixBase.png -------------------------------------------------------------------------------- /images/GetStatus-Command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/GetStatus-Command.png -------------------------------------------------------------------------------- /images/GetStatus-Result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/GetStatus-Result.png -------------------------------------------------------------------------------- /images/Initialize-Command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/Initialize-Command.png -------------------------------------------------------------------------------- /images/InitializeCustom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/InitializeCustom.png -------------------------------------------------------------------------------- /images/InitializeSettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/InitializeSettings.png -------------------------------------------------------------------------------- /images/StartFeatureBase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/StartFeatureBase.png -------------------------------------------------------------------------------- /images/StartHotfixMaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/StartHotfixMaster.png -------------------------------------------------------------------------------- /images/StartReleaseBase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/StartReleaseBase.png -------------------------------------------------------------------------------- /images/DeletionConfigValues.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/DeletionConfigValues.png -------------------------------------------------------------------------------- /images/StartFeature-Command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/StartFeature-Command.png -------------------------------------------------------------------------------- /images/StartFeatureDevelop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/StartFeatureDevelop.png -------------------------------------------------------------------------------- /images/StartHotfix-Command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/StartHotfix-Command.png -------------------------------------------------------------------------------- /images/StartRelease-Command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/StartRelease-Command.png -------------------------------------------------------------------------------- /images/StartReleaseDevelop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/StartReleaseDevelop.png -------------------------------------------------------------------------------- /images/FinishFeature-Statusbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/FinishFeature-Statusbar.png -------------------------------------------------------------------------------- /images/FinishHotfix-Statusbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/FinishHotfix-Statusbar.png -------------------------------------------------------------------------------- /images/FinishRelease-Statusbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/FinishRelease-Statusbar.png -------------------------------------------------------------------------------- /images/InitializeWithDefault.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaggy13spe/gitflow4code/HEAD/images/InitializeWithDefault.png -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | typings/** 3 | out/test/** 4 | test/** 5 | src/** 6 | **/*.map 7 | .gitignore 8 | tsconfig.json 9 | vsc-extension-quickstart.md 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "." 11 | }, 12 | "exclude": [ 13 | "node_modules", 14 | ".vscode-test" 15 | ] 16 | } -------------------------------------------------------------------------------- /src/settings/branchSettings.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export class BranchSetting { 4 | 5 | constructor(name: string, base: string, pushAfterFinishing?: boolean) { 6 | this.name = name; 7 | this.base = base; 8 | if(pushAfterFinishing) 9 | this.pushAfterFinishing = pushAfterFinishing; 10 | } 11 | 12 | name: string = ''; 13 | base: string = ''; 14 | pushAfterFinishing: boolean; 15 | } -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export interface IDisposable { 4 | dispose(): void; 5 | } 6 | 7 | export function dispose(disposables: T[]): T[] { 8 | disposables.forEach(d => d.dispose()); 9 | return []; 10 | } 11 | 12 | export function toDisposable(dispose: () => void): IDisposable { 13 | return { dispose }; 14 | } 15 | 16 | export function combinedDisposable(disposables: IDisposable[]): IDisposable { 17 | return toDisposable(() => dispose(disposables)); 18 | } 19 | 20 | export const EmptyDisposable = toDisposable(() => null); -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /src/settings/configSettings.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export class InitConfigSettings { 4 | 5 | constructor(master: string, develop: string, features: string, releases: string, hotfixes: string) { 6 | this.master = master; 7 | this.develop = develop; 8 | this.features = features; 9 | this.releases = releases; 10 | this.hotfixes = hotfixes; 11 | } 12 | 13 | develop: string = ''; 14 | master: string = ''; 15 | releases: string = ''; 16 | hotfixes: string = ''; 17 | features: string = ''; 18 | support: string = ''; 19 | } -------------------------------------------------------------------------------- /src/view.ts: -------------------------------------------------------------------------------- 1 | import {StatusBarItem} from 'vscode'; 2 | 3 | export interface IView { 4 | /** 5 | * Refresh the view 6 | */ 7 | refresh(text: string): void; 8 | } 9 | 10 | export class StatusBarView implements IView { 11 | private _statusBarItem: StatusBarItem; 12 | 13 | constructor(statusBarItem: StatusBarItem) { 14 | this._statusBarItem = statusBarItem; 15 | this._statusBarItem.command = "extension.gitflow4code"; 16 | } 17 | 18 | refresh(text: string) { 19 | this._statusBarItem.text = 'Finish feature'; 20 | this._statusBarItem.show(); 21 | } 22 | } -------------------------------------------------------------------------------- /test/features.test.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Note: This example test is leveraging the Mocha test framework. 3 | // Please refer to their documentation on https://mochajs.org/ for help. 4 | // 5 | 6 | // The module 'assert' provides assertion methods from node 7 | import * as assert from 'assert'; 8 | import * as features from '../src/commands/features'; 9 | 10 | // Defines a Mocha test suite to group tests of similar kind together 11 | suite("Features Tests", function () { 12 | 13 | // Defines a Mocha unit test 14 | test("Show picklist for features", function() { 15 | // features.run(null); 16 | }); 17 | }); -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | // 2 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING 3 | // 4 | // This file is providing the test runner to use when running extension tests. 5 | // By default the test runner in use is Mocha based. 6 | // 7 | // You can provide your own test runner if you want to override it by exporting 8 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension 9 | // host can call to run the tests. The test runner is expected to use console.log 10 | // to report the results back to the caller. When the tests are finished, return 11 | // a possible error to the callback or null if none. 12 | 13 | var testRunner = require('vscode/lib/testrunner'); 14 | 15 | // You can directly control Mocha options by uncommenting the following lines 16 | // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info 17 | testRunner.configure({ 18 | ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) 19 | useColors: true // colored output from test results 20 | }); 21 | 22 | module.exports = testRunner; -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.1.0", 4 | "configurations": [ 5 | { 6 | "name": "Launch Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], 11 | "stopOnEntry": false, 12 | "sourceMaps": true, 13 | "outDir": "${workspaceRoot}/out/src", 14 | "preLaunchTask": "npm: watch" 15 | }, 16 | { 17 | "name": "Launch Tests", 18 | "type": "extensionHost", 19 | "request": "launch", 20 | "runtimeExecutable": "${execPath}", 21 | "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ], 22 | "stopOnEntry": false, 23 | "sourceMaps": true, 24 | "outDir": "${workspaceRoot}/out/test", 25 | "preLaunchTask": "npm: watch" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Michael Morrison 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # node-waf configuration 17 | .lock-wscript 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | #Dirs 23 | out 24 | node_modules 25 | 26 | #VSCode Settings files 27 | .vscode/settings.json 28 | 29 | # Compiled source # 30 | ################### 31 | *.com 32 | *.class 33 | # Logs 34 | logs 35 | *.log 36 | *.dll 37 | *.exe 38 | *.o 39 | *.so 40 | *.lock 41 | *.bin 42 | *.aar 43 | 44 | # Packages # 45 | ############ 46 | # it's better to unpack these files and commit the raw source 47 | # git has its own built in compression methods 48 | *.7z 49 | *.dmg 50 | *.gz 51 | *.iso 52 | *.rar 53 | *.tar 54 | *.zip 55 | 56 | # Logs and databases # 57 | ###################### 58 | *.log 59 | *.sql 60 | *.sqlite 61 | 62 | # OS generated files # 63 | ###################### 64 | .DS_Store 65 | .DS_Store? 66 | ._* 67 | .Spotlight-V100 68 | .Trashes 69 | ehthumbs.db 70 | Thumbs.db 71 | 72 | *.dll 73 | *.exe 74 | *.o 75 | *.so 76 | *.lock 77 | *.bin 78 | *.aar 79 | 80 | -------------------------------------------------------------------------------- /src/commands/gitCommands.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as gitUtils from '../helpers/gitUtils'; 3 | import * as path from 'path'; 4 | 5 | export function run(outChannel) { 6 | if(!vscode.window.activeTextEditor || !vscode.window.activeTextEditor.document) 7 | return; 8 | 9 | gitUtils.getGitRepositoryPath(vscode.workspace.rootPath).then(function (gitRepositoryPath) { 10 | gitUtils.getStatus(gitRepositoryPath).then(showStatus, genericErrorHandler); 11 | function showStatus(log) { 12 | if(log.length === 0) { 13 | vscode.window.showInformationMessage('Nothing to show'); 14 | return; 15 | } 16 | 17 | outChannel.append(log); 18 | outChannel.show(); 19 | } 20 | function genericErrorHandler(error) { 21 | if(error.code && error.syscall && error.code === 'ENOENT' && error.syscall === 'spawn git') 22 | vscode.window.showErrorMessage('Cannot find git installation'); 23 | else { 24 | outChannel.appendLine(error); 25 | outChannel.show(); 26 | vscode.window.showErrorMessage('There was an error, please view details in output log'); 27 | } 28 | } 29 | }).catch(function (error) { 30 | if(error.code && error.syscall && error.code === 'ENOENT' && error.syscall === 'spawn git') 31 | vscode.window.showErrorMessage('Cannot find git installation'); 32 | else { 33 | outChannel.appendLine(error); 34 | outChannel.show(); 35 | vscode.window.showErrorMessage('There was an error, please view details in output log'); 36 | } 37 | }); 38 | } -------------------------------------------------------------------------------- /vsc-extension-quickstart.md: -------------------------------------------------------------------------------- 1 | # Welcome to your first VS Code Extension 2 | 3 | ## What's in the folder 4 | * This folder contains all of the files necessary for your extension 5 | * `package.json` - this is the manifest file in which you declare your extension and command. 6 | The sample plugin registers a command and defines its title and command name. With this information 7 | VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. 8 | * `src/extension.ts` - this is the main file where you will provide the implementation of your command. 9 | The file exports one function, `activate`, which is called the very first time your extension is 10 | activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. 11 | We pass the function containing the implementation of the command as the second parameter to 12 | `registerCommand`. 13 | 14 | ## Get up and running straight away 15 | * press `F5` to open a new window with your extension loaded 16 | * run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World` 17 | * set breakpoints in your code inside `src/extension.ts` to debug your extension 18 | * find output from your extension in the debug console 19 | 20 | ## Make changes 21 | * you can relaunch the extension from the debug toolbar after changing code in `src/extension.ts` 22 | * you can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes 23 | 24 | ## Explore the API 25 | * you can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts` 26 | 27 | ## Run tests 28 | * open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Launch Tests` 29 | * press `F5` to run the tests in a new window with your extension loaded 30 | * see the output of the test result in the debug console 31 | * make changes to `test/extension.test.ts` or create new test files inside the `test` folder 32 | * by convention, the test runner will only consider files matching the name pattern `**.test.ts` 33 | * you can create folders inside the `test` folder to structure your tests any way you want -------------------------------------------------------------------------------- /src/commands/init.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { workspace } from 'vscode'; 3 | import * as gitflowUtils from '../helpers/gitflowUtils'; 4 | import * as gitUtils from '../helpers/gitUtils'; 5 | import * as path from 'path'; 6 | import { InitConfigSettings } from '../settings/configSettings'; 7 | 8 | export function run(outChannel) { 9 | let configSettings = new InitConfigSettings('master', 'develop', 'feature/', 'release/', 'hotfix/'); 10 | var itemPickList = [ 11 | { 12 | label: "Initialize with defaults", 13 | description: "Initialize gitflow with [develop], [master], [feature/], [release/], and [hotfix/]" 14 | }, 15 | { 16 | label: "Initialize with custom values", 17 | description: "Initialize gitflow with custom values" 18 | } 19 | ]; 20 | vscode.window.showQuickPick(itemPickList).then(function(item) { 21 | if(!item) return; 22 | 23 | outChannel.clear(); 24 | if(item.label === itemPickList[0].label) 25 | initializeWithDefaults(outChannel); 26 | else { 27 | outChannel.clear(); 28 | vscode.window.showInputBox({ prompt: 'Branch name for production releases: [master]', ignoreFocusOut: true}).then(val => setMaster(outChannel, configSettings, val)); 29 | } 30 | }); 31 | } 32 | 33 | function setMaster(outChannel, configSettings, val) { 34 | if(val !== '') 35 | configSettings.master = val; 36 | vscode.window.showInputBox({ prompt: 'Branch name for "next release" development: [develop]', ignoreFocusOut: true}).then(val => setDevelop(outChannel, configSettings, val)); 37 | } 38 | 39 | function setDevelop(outChannel, configSettings, val) { 40 | if(val !== '') 41 | configSettings.develop = val; 42 | vscode.window.showInputBox({ prompt: 'Feature branch development: [feature/]', ignoreFocusOut: true}).then(val => setFeature(outChannel, configSettings, val)); 43 | } 44 | 45 | function setFeature(outChannel, configSettings, val) { 46 | if(val !== '') 47 | configSettings.features = val; 48 | vscode.window.showInputBox({ prompt: 'Release branch development: [release/]', ignoreFocusOut: true}).then(val => setRelease(outChannel, configSettings, val)); 49 | } 50 | 51 | function setRelease(outChannel, configSettings, val) { 52 | if(val !== '') 53 | configSettings.releases = val; 54 | vscode.window.showInputBox({ prompt: 'Hotfix branch development: [hotfix/]', ignoreFocusOut: true}).then(val => setHotfix(outChannel, configSettings, val)); 55 | } 56 | 57 | function setHotfix(outChannel, configSettings, val) { 58 | if(val !== '') 59 | configSettings.hotfixes = val; 60 | 61 | const config = workspace.getConfiguration(); 62 | const configValues = config.get('gitflow4code.init') as InitConfigSettings; 63 | config.update('gitflow4code.init', configSettings, vscode.ConfigurationTarget.Workspace); 64 | gitUtils.getGitRepositoryPath(vscode.workspace.rootPath).then(function(getGitRepositoryPath) { 65 | gitflowUtils.initializeRepository(getGitRepositoryPath).then(initializeSuccess, genericErrorHandler); 66 | function initializeSuccess(log) { 67 | if(log.length === 0) { 68 | vscode.window.showInformationMessage('Nothing to show'); 69 | return; 70 | } 71 | 72 | outChannel.append(log); 73 | outChannel.show(); 74 | } 75 | }).catch(genericErrorHandler).catch(genericErrorHandler); 76 | 77 | function genericErrorHandler(error) { 78 | if(error.code && error.syscall && error.code === 'ENOENT' && error.syscall === 'spawn git') 79 | vscode.window.showErrorMessage('Cannot find git installation'); 80 | else { 81 | outChannel.appendLine(error); 82 | outChannel.show(); 83 | vscode.window.showErrorMessage('There was an error, please view details in output log'); 84 | } 85 | } 86 | } 87 | 88 | function initializeWithDefaults(outChannel) { 89 | gitUtils.getGitRepositoryPath(vscode.workspace.rootPath).then(function(getGitRepositoryPath) { 90 | gitflowUtils.initializeRepository(getGitRepositoryPath).then(initializeSuccess, genericErrorHandler); 91 | function initializeSuccess(log) { 92 | if(log.length === 0) { 93 | vscode.window.showInformationMessage('Nothing to show'); 94 | return; 95 | } 96 | 97 | outChannel.append(log); 98 | outChannel.show(); 99 | } 100 | }).catch(genericErrorHandler).catch(genericErrorHandler); 101 | 102 | function genericErrorHandler(error) { 103 | if(error.code && error.syscall && error.code === 'ENOENT' && error.syscall === 'spawn git') 104 | vscode.window.showErrorMessage('Cannot find git installation'); 105 | else { 106 | outChannel.appendLine(error); 107 | outChannel.show(); 108 | vscode.window.showErrorMessage('There was an error, please view details in output log'); 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## [1.2.5] 8 | ### Added 9 | - [Issue #11](https://github.com/Shaggy13spe/gitflow4code/issues/11) Added ability to configure if feature base branches are automatically pushed to origin after finishing and merging them 10 | ### Fixed 11 | - Merged Pull Request for [Issue #17](https://github.com/Shaggy13spe/gitflow4code/pull/17). Fix for allowing dot '.' in git branch name path as long as it is not the beginning of the path. Thanks to [Vasily Pikulev](https://github.com/pikulev) 12 | 13 | ## [1.2.4] 14 | ### Changed 15 | - Upgraded TypeScript compilation to task version 2.0.0 per [VS Code authors' request](https://code.visualstudio.com/docs/extensions/developing-extensions#_compiling-typescript) 16 | 17 | ## [1.2.3] - 2018-6-4 18 | ### Fixed 19 | - [Issue #12](https://github.com/Shaggy13spe/gitflow4code/issues/12) Fixed hotfix branches not being merged in to master when finished 20 | 21 | ## [1.2.2] - 2018-5-30 22 | ### Added 23 | - [Issue #7](https://github.com/Shaggy13spe/gitflow4code/issues/7) Ability to switch applications while input box is shown and not having it disappear 24 | ### Fixed 25 | - [Issue #10](https://github.com/Shaggy13spe/gitflow4code/issues/10) Fixed when naming a branch with spaces caused bad merging in to develop instead of defined base branch 26 | 27 | ## [1.2.1] - 2018-4-29 28 | ### Added 29 | - [Issue #6](https://github.com/Shaggy13spe/gitflow4code/issues/6) New configuration value, showStatusBarFinisher. Defaulted to true, set to false to not show the new "Finish" status bar button introduced in 1.2.0 release. Given that there may be many extensions installed that utilize the status bar, it can become quite crowded, this allows you to make it a little less so if you find the button not really needed in your set up. 30 | 31 | ## [1.2.0] - 2018-4-25 32 | ### Added 33 | - New statusbar button to quickly finish features, releases, or hotfixes 34 | - New keyboard shortcuts for quickly running commands 35 | - New configuration value, askBeforeDeletion. When finishing a feature, release, and/or hotfix, you will now be asked if you would like to delete the branch (only local not remote if you have pushed it). Setting is true by default. If you set to false, it will follow the behavior of the deleteBranch setting. 36 | - New configuration value, deleteBranchByDefault. When finishing a feature, release, and/or hotfix, this setting will determine if the branch is deleted by default or not. Setting is false by default. If you set to true, and askBeforeDeletion is set to false, then branches will be deleted without question. 37 | ### Changed 38 | - In support of the new statusbar button, the command menu structure had to be changed somewhat. Starting & Finishing features, releases, and/or hotfixes commands are now listed on the command menu (Cmd+P/Ctrl+P), as are Initialize and Git Status. 39 | 40 | ## [1.1.0] - 2018-4-16 41 | ### Added 42 | - [Issue #3](https://github.com/Shaggy13spe/gitflow4code/issues/3) Ability to create release and/or hotfix branches based on branches other than default branch ("development" and "production" respectively) 43 | ### Changed 44 | - [Issue #3](https://github.com/Shaggy13spe/gitflow4code/issues/3) Ability to now create feature branches based on branches other than "development" branch, prior release only allowed to create features off of either development or another "feature" 45 | 46 | ## [1.0.0] - 2017-9-14 47 | ### Added 48 | - Ability to create feature branches based on branches other than "development" branch 49 | 50 | ### Changed 51 | - Edited the README.md to be more robust on how to use the extension 52 | 53 | ## [0.1.3] - 2017-8-31 54 | ### Fixed 55 | - Error when finishing Release or Hotfix with tagging 56 | 57 | ## [0.1.2] - 2017-8-31 58 | ### Added 59 | - Ability to initialize git flow repository settings using custom values 60 | 61 | ## [0.1.1] - 2017-8-30 62 | ### Added 63 | - Ability to initialize git flow repository settings 64 | 65 | ### Changed 66 | - Removed need for gitflow or gitflow-avh to be installed prior to using the extension. All commands now run organic git commands 67 | 68 | ### Fixed 69 | - [Issue #1](https://github.com/Shaggy13spe/gitflow4code/issues/1) Error that occurred when trying to use the extension that claimed there wasn't a git repo in the current or any parent folder 70 | 71 | ## [0.0.5] - 2017-1-26 72 | ### Added 73 | - This CHANGELOG file. 74 | 75 | ### Changed 76 | - Updated README file. 77 | - Updated package.json to add keywords and badges 78 | 79 | ## [0.0.4] - 2017-1-26 80 | ### Fixed 81 | - Hotfixes require a TAG to be defined, but extension didn't support it, thus it wouldn't finish releases 82 | 83 | ## 0.0.3 [UNRELEASED] 84 | ### Fixed 85 | - Releases require a TAG to be defined, but extension didn't support it, thus it wouldn't finish releases 86 | 87 | ## [0.0.2] - 2016-6-28 88 | ### Added 89 | - Functionality to check for illegal characters in branch names 90 | 91 | 92 | ### Changed 93 | - How git directory is found. No longer need to have code file open 94 | 95 | ## [0.0.1] - 2016-5-3 96 | ### Added 97 | - First version of extension 98 | - Allowed for creation of features, releases, hotfixes 99 | - Functionality to convert spaces to _ in branch names 100 | 101 | -------------------------------------------------------------------------------- /src/helpers/gitUtils.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import * as vscode from 'vscode'; 3 | import { workspace, window, commands, extensions } from 'vscode'; 4 | import * as path from 'path'; 5 | import * as child_process_1 from 'child_process'; 6 | import * as fs from 'fs'; 7 | 8 | const api = extensions.getExtension('vscode.git').exports; 9 | 10 | export function getGitRepositoryPath(fileName) { 11 | fileName += '/.'; 12 | return api.getGitPath().then(function (gitExecutable) { 13 | return new Promise(function (resolve, reject) { 14 | let options = { cwd: path.dirname(fileName) }; 15 | let util = require('util'); 16 | let spawn = require('child_process').spawn; 17 | let ls = spawn(gitExecutable, ['rev-parse', '--git-dir'], options); 18 | let log = ''; 19 | let error = ''; 20 | ls.stdout.on('data', function (data) { 21 | log =+ data + '\n'; 22 | }); 23 | ls.stderr.on('data', function (data) { 24 | error += data; 25 | }); 26 | ls.on('exit', function (code) { 27 | if(error.length > 0) { 28 | reject(error); 29 | return; 30 | } 31 | if(path.dirname(log) === '.') { 32 | log = path.dirname(fileName) + '/' + log; 33 | } 34 | resolve(path.dirname(log)); 35 | }); 36 | }); 37 | }); 38 | } 39 | 40 | export function getStatus(rootDir) { 41 | return api.getGitPath().then(function (gitExecutable) { 42 | return new Promise(function (resolve, reject) { 43 | let options = { cwd: rootDir }; 44 | let util = require('util'); 45 | let spawn = require('child_process').spawn; 46 | let ls = spawn(gitExecutable, ['status'], options); 47 | let log = ''; 48 | let error = ''; 49 | ls.stdout.on('data', function (data) { 50 | log += data + '\n'; 51 | }); 52 | ls.stderr.on('data', function (data) { 53 | error += data; 54 | }); 55 | ls.on('exit', function (data) { 56 | if(error.length > 0) { 57 | reject(error); 58 | return; 59 | } 60 | resolve(log); 61 | }); 62 | }); 63 | }); 64 | } 65 | 66 | export function getCurrentBranchName(rootDir) { 67 | return api.getGitPath().then((gitExecutable) => { 68 | return new Promise(function (resolve, reject) { 69 | var options = { cwd: rootDir }; 70 | var util = require('util'); 71 | var spawn = require('child_process').spawn; 72 | var ls = spawn(gitExecutable, ['rev-parse', '--abbrev-ref', 'HEAD'], options); 73 | var log = ''; 74 | var error = ''; 75 | ls.stdout.on('data', function (data) { 76 | log += data + '\n'; 77 | }); 78 | ls.stderr.on('data', function (data) { 79 | error += data; 80 | }); 81 | ls.on('exit', function (data) { 82 | if (error.length > 0) { 83 | reject(error); 84 | return; 85 | } 86 | resolve(log.replace(/ /g,'').trim().split('\n')[0]); 87 | }); 88 | }); 89 | }); 90 | } 91 | 92 | export function checkForBranch(rootDir, branchName) { 93 | return api.getGitPath().then(function (gitExecutable) { 94 | return new Promise(function (resolve, reject) { 95 | let options = { cwd: rootDir }; 96 | let spawn = require('child_process').spawn; 97 | let ls = spawn(gitExecutable, ['show-ref', '--verify', '--quiet', 'refs/heads/' + branchName], options); 98 | ls.on('exit', function (code) { 99 | resolve(code === 0); 100 | }); 101 | }); 102 | }); 103 | } 104 | 105 | export function getBranchList(fileName) { 106 | return getGitRepositoryPath(fileName).then((rootDir) => { 107 | return api.getGitPath().then(function (gitExecutable) { 108 | return new Promise(function (resolve, reject) { 109 | let options = { cwd: rootDir }; 110 | let util = require('util'); 111 | let spawn = require('child_process').spawn; 112 | let ls = spawn(gitExecutable, ['branch'], options); 113 | let log = ''; 114 | let error = ''; 115 | ls.stdout.on('data', function (data) { 116 | log += data; 117 | 118 | }); 119 | ls.stderr.on('data', function (data) { 120 | error += data; 121 | }); 122 | ls.on('exit', function (data) { 123 | if(error.length > 0) { 124 | reject(error); 125 | return; 126 | } 127 | 128 | var branchList = log.split('\n') as string[]; 129 | 130 | var filteredBranchList = branchList.map((value) => { 131 | return value.replace('*', '').trim(); 132 | }).filter(x => !!x); 133 | 134 | resolve(filteredBranchList); 135 | }); 136 | }); 137 | }); 138 | }); 139 | } -------------------------------------------------------------------------------- /src/gitflow.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // The module 'vscode' contains the VS Code extensibility API 3 | // Import the module and reference it with the alias vscode in your code below 4 | import * as vscode from 'vscode'; 5 | import { workspace, window, commands, extensions, Disposable } from 'vscode'; 6 | import * as initCommands from './commands/init'; 7 | import * as featureCommands from './commands/features'; 8 | import * as releaseCommands from './commands/releases'; 9 | import * as hotfixCommands from './commands/hotfixes'; 10 | import * as gitCommands from './commands/gitCommands'; 11 | import { InitConfigSettings } from './settings/configSettings'; 12 | import { getCurrentBranchName } from '../src/helpers/gitUtils'; 13 | import { toDisposable } from './util'; 14 | 15 | const config = workspace.getConfiguration(); 16 | const api = extensions.getExtension('vscode.git').exports; 17 | const initValues = config.get('gitflow4code.init') as InitConfigSettings; 18 | const showStatusBarFinisher = config.get('gitflow4code.showStatusBarFinisher') as boolean; 19 | 20 | async function init(context: vscode.ExtensionContext, disposables: Disposable[]): Promise { 21 | const outChannel = window.createOutputChannel('GitFlow4Code'); 22 | disposables.push(outChannel); 23 | 24 | if(showStatusBarFinisher) { 25 | let statusFinisher = new FinishStatusItem(); 26 | context.subscriptions.push(statusFinisher); 27 | } 28 | 29 | let initializeCommand = vscode.commands.registerCommand('gitflow.initialize', () => { initCommands.run(outChannel); }); 30 | let startFeatureCommand = vscode.commands.registerCommand('gitflow.startFeature', () => { featureCommands.run(outChannel, 'start'); }); 31 | let finishFeatureCommand = vscode.commands.registerCommand('gitflow.finishFeature', () => { featureCommands.run(outChannel, 'finish'); }); 32 | let startReleaseCommand = vscode.commands.registerCommand('gitflow.startRelease', () => { releaseCommands.run(outChannel, 'start'); }); 33 | let finishReleaseCommand = vscode.commands.registerCommand('gitflow.finishRelease', () => { releaseCommands.run(outChannel, 'finish'); }); 34 | let startHotfixCommand = vscode.commands.registerCommand('gitflow.startHotfix', () => { hotfixCommands.run(outChannel, 'start'); }); 35 | let releaseHotfixCommand = vscode.commands.registerCommand('gitflow.finishHotfix', () => { hotfixCommands.run(outChannel, 'finish'); }); 36 | let gitStatusCommand = vscode.commands.registerCommand('gitflow.gitStatus', () => { gitCommands.run(outChannel); }); 37 | 38 | context.subscriptions.push(initializeCommand); 39 | context.subscriptions.push(startFeatureCommand); 40 | context.subscriptions.push(finishFeatureCommand); 41 | context.subscriptions.push(startReleaseCommand); 42 | context.subscriptions.push(finishReleaseCommand); 43 | context.subscriptions.push(startHotfixCommand); 44 | context.subscriptions.push(releaseHotfixCommand); 45 | context.subscriptions.push(gitStatusCommand); 46 | 47 | } 48 | 49 | // this method is called when your extension is activated 50 | // your extension is activated the very first time the command is executed 51 | export function activate(context: vscode.ExtensionContext): any { 52 | const disposables: Disposable[] = []; 53 | context.subscriptions.push(new Disposable(() => Disposable.from(...disposables).dispose())); 54 | 55 | init(context, disposables).catch(err => console.error(err)); 56 | 57 | if(showStatusBarFinisher){ 58 | let statusFinisher = new FinishStatusItem(); 59 | getCurrentBranchName(vscode.workspace.rootPath).then((branchName) => { 60 | if(branchName.toString().startsWith(initValues.features) 61 | || branchName.toString().startsWith(initValues.releases) 62 | || branchName.toString().startsWith(initValues.hotfixes)) { 63 | 64 | statusFinisher.showStatusItem(branchName); 65 | } 66 | else 67 | statusFinisher.closeStatusItem(); 68 | }); 69 | 70 | // create file watcher to see if ./.git/HEAD has changed. If so, this an indication 71 | // that the branch has changed 72 | const watcher = workspace.createFileSystemWatcher('**/.git/HEAD'); 73 | 74 | watcher.onDidChange(() => { 75 | getCurrentBranchName(vscode.workspace.rootPath).then((branchName) => { 76 | if(branchName.toString().startsWith(initValues.features) 77 | || branchName.toString().startsWith(initValues.releases) 78 | || branchName.toString().startsWith(initValues.hotfixes)) { 79 | 80 | statusFinisher.showStatusItem(branchName); 81 | } 82 | else 83 | statusFinisher.closeStatusItem(); 84 | }); 85 | }); 86 | } 87 | } 88 | 89 | class FinishStatusItem { 90 | private _statusBarItem: vscode.StatusBarItem; 91 | 92 | public showStatusItem(branchName) { 93 | if(!this._statusBarItem) 94 | this._statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); 95 | 96 | this._statusBarItem.text = '$(git-merge) Finish ' + branchName; 97 | 98 | let command = ''; 99 | if(branchName.toString().startsWith(initValues.features)) 100 | command = 'gitflow.finishFeature'; 101 | else if(branchName.toString().startsWith(initValues.releases)) 102 | command = 'gitflow.finishRelease'; 103 | else if(branchName.toString().startsWith(initValues.hotfixes)) 104 | command = 'gitflow.finishHotfix'; 105 | 106 | this._statusBarItem.command = command; 107 | this._statusBarItem.show(); 108 | } 109 | 110 | public closeStatusItem() { 111 | this._statusBarItem.hide(); 112 | } 113 | 114 | dispose() { 115 | this._statusBarItem.dispose(); 116 | } 117 | 118 | } 119 | 120 | // this method is called when your extension is deactivated 121 | export function deactivate() { 122 | } -------------------------------------------------------------------------------- /src/commands/hotfixes.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { workspace } from 'vscode'; 3 | import * as gitflowUtils from '../helpers/gitflowUtils'; 4 | import * as gitUtils from '../helpers/gitUtils'; 5 | import * as path from 'path'; 6 | import { InitConfigSettings } from '../settings/configSettings'; 7 | import { BranchSetting } from '../settings/branchSettings'; 8 | 9 | const config = workspace.getConfiguration(); 10 | const initValues = config.get('gitflow4code.init') as InitConfigSettings; 11 | const askForDeletion = config.get('gitflow4code.askBeforeDeletion') as boolean; 12 | const deleteByDefault = config.get('gitflow4code.deleteBranchByDefault') as boolean; 13 | 14 | export function run(outChannel, action) { 15 | if(action === 'start') { 16 | var itemPickList = [ 17 | { 18 | label: 'Start Hotfix from ' + initValues.master, 19 | description: '' 20 | }, 21 | { 22 | label: 'Start Hotfix from another base branch', 23 | description: '' 24 | } 25 | ]; 26 | vscode.window.showQuickPick(itemPickList).then(function(item) { 27 | if(!item) return; 28 | 29 | outChannel.clear(); 30 | if(item.label === itemPickList[0].label) 31 | vscode.window.showInputBox({ prompt: 'Name of Hotfix: ', ignoreFocusOut: true }).then(val => startHotfix(outChannel, val, initValues.master)); 32 | else if(item.label === itemPickList[1].label) 33 | gitUtils.getBranchList(workspace.rootPath).then((hotfixes) => { 34 | var branchPickList = []; 35 | hotfixes.forEach(branchName => { 36 | branchPickList.push( { label: branchName, description: 'create hotfix branch using ' + branchName + ' as your base'}); 37 | }); 38 | 39 | vscode.window.showQuickPick(branchPickList).then(function(item) { 40 | if(!item) return; 41 | 42 | outChannel.clear(); 43 | vscode.window.showInputBox({ prompt: 'Name of Hotfix: ', ignoreFocusOut: true }).then(val => startHotfix(outChannel, val, item.label)); 44 | }); 45 | }); 46 | }); 47 | } 48 | else if(action === 'finish') { 49 | if(askForDeletion) 50 | vscode.window.showInputBox({ prompt: 'Tag this hotfix with: ', ignoreFocusOut: true }).then(function(tag) { 51 | vscode.window.showInputBox({ prompt: 'Would you like this hotfix branch deleted after finishing? (y/n)', ignoreFocusOut: true }).then(function(val) { 52 | if(val !== undefined && (val.toLowerCase() === 'y' || val.toLowerCase() === 'n')) { 53 | var deleteBranch = val.toLowerCase() === 'y'; 54 | finishHotfix(outChannel, tag, val); 55 | } 56 | }); 57 | }); 58 | else 59 | vscode.window.showInputBox({ prompt: 'Tag this hotfix with: ', ignoreFocusOut: true }).then(tag => finishHotfix(outChannel, tag, deleteByDefault)); 60 | } 61 | } 62 | 63 | function startHotfix(outChannel, hotfixName, baseBranch) { 64 | if(hotfixName !== undefined) // User chose to Cancel/Esc operation 65 | if(hotfixName !== '') { 66 | hotfixName = hotfixName.trim().replace(/ /g, '_'); 67 | gitUtils.getGitRepositoryPath(vscode.workspace.rootPath).then(function (gitRepositoryPath) { 68 | gitflowUtils.startHotfix(gitRepositoryPath, hotfixName, baseBranch) 69 | .then(startHotfix, genericErrorHandler) 70 | .catch(genericErrorHandler) 71 | }).catch(genericErrorHandler); 72 | } 73 | else 74 | genericErrorHandler('Name of hotfix cannot be blank'); 75 | 76 | function startHotfix(log) { 77 | if(log.length === 0) { 78 | vscode.window.showInformationMessage('Nothing to show'); 79 | return; 80 | } 81 | 82 | let hotfixesConfig = config.get('gitflow4code.hotfixes') as BranchSetting[]; 83 | hotfixesConfig.push(new BranchSetting(initValues.hotfixes + hotfixName, baseBranch)); 84 | config.update('gitflow4code.hotfixes', hotfixesConfig); 85 | 86 | outChannel.append(log); 87 | outChannel.show(); 88 | } 89 | function genericErrorHandler(error) { 90 | if(error.code && error.syscall && error.code === 'ENOENT' && error.syscall === 'spawn git') 91 | vscode.window.showErrorMessage('Cannot find git installation'); 92 | else { 93 | outChannel.appendLine(error); 94 | outChannel.show(); 95 | vscode.window.showErrorMessage('There was an error, please view details in output log'); 96 | } 97 | } 98 | } 99 | 100 | function finishHotfix(outChannel, hotfixTag, deleteBranch) { 101 | gitUtils.getGitRepositoryPath(vscode.workspace.rootPath).then(function (gitRepositoryPath) { 102 | gitUtils.getCurrentBranchName(vscode.workspace.rootPath).then((branchName) => { 103 | let hotfixesConfig = config.get('gitflow4code.hotfixes') as BranchSetting[]; 104 | let hotfixSetting = hotfixesConfig.find((hotfix) => hotfix.name === branchName.toString()); 105 | if(!hotfixSetting) 106 | hotfixSetting = new BranchSetting(branchName.toString(), initValues.master); 107 | gitflowUtils.finishHotfix(gitRepositoryPath, hotfixSetting.base, hotfixTag, deleteBranch).then(finishHotfix, genericErrorHandler); 108 | function finishHotfix(log) { 109 | if(log.length === 0) { 110 | vscode.window.showInformationMessage('Nothing to show'); 111 | return; 112 | } 113 | 114 | if(deleteBranch) { 115 | let hotfixIndex = hotfixesConfig.indexOf(hotfixSetting); 116 | hotfixesConfig.splice(hotfixIndex, 1); 117 | config.update('gitflow4code.hotfixes', hotfixesConfig); 118 | } 119 | 120 | outChannel.append(log); 121 | outChannel.show(); 122 | } 123 | }) 124 | }).catch(genericErrorHandler); 125 | 126 | function genericErrorHandler(error) { 127 | if(error.code && error.syscall && error.code === 'ENOENT' && error.syscall === 'spawn git') 128 | vscode.window.showErrorMessage('Cannot find git installation'); 129 | else { 130 | outChannel.appendLine(error); 131 | outChannel.show(); 132 | vscode.window.showErrorMessage('There was an error, please view details in output log'); 133 | } 134 | } 135 | } -------------------------------------------------------------------------------- /src/commands/releases.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { workspace } from 'vscode'; 3 | import * as gitflowUtils from '../helpers/gitflowUtils'; 4 | import * as gitUtils from '../helpers/gitUtils'; 5 | import * as path from 'path'; 6 | import { InitConfigSettings } from '../settings/configSettings'; 7 | import { BranchSetting } from '../settings/branchSettings'; 8 | 9 | const config = workspace.getConfiguration(); 10 | const initValues = config.get('gitflow4code.init') as InitConfigSettings; 11 | const askForDeletion = config.get('gitflow4code.askBeforeDeletion') as boolean; 12 | const deleteByDefault = config.get('gitflow4code.deleteBranchByDefault') as boolean; 13 | 14 | export function run(outChannel, action) { 15 | if(action === 'start') { 16 | var itemPickList = [ 17 | { 18 | label: 'Start Release from ' + initValues.develop, 19 | description: '' 20 | }, 21 | { 22 | label: 'Start Release from another base branch', 23 | description: '' 24 | } 25 | ]; 26 | 27 | vscode.window.showQuickPick(itemPickList).then(function(item) { 28 | if(!item) return; 29 | 30 | outChannel.clear(); 31 | if(item.label === itemPickList[0].label) 32 | vscode.window.showInputBox({ prompt: 'Name of Release: ', ignoreFocusOut: true }).then(val => startRelease(outChannel, val, initValues.develop)); 33 | else if(item.label === itemPickList[1].label) 34 | gitUtils.getBranchList(workspace.rootPath).then((releases) => { 35 | var branchPickList = []; 36 | releases.forEach(branchName => { 37 | branchPickList.push( { label: branchName, description: 'create release branch using ' + branchName + ' as your base'}); 38 | }); 39 | 40 | vscode.window.showQuickPick(branchPickList).then(function(item) { 41 | if(!item) return; 42 | 43 | outChannel.clear(); 44 | vscode.window.showInputBox({ prompt: 'Name of Release: ', ignoreFocusOut: true }).then(val => startRelease(outChannel, val, item.label)); 45 | }); 46 | }); 47 | else { 48 | } 49 | }); 50 | } 51 | else if (action === 'finish') { 52 | if(askForDeletion) 53 | vscode.window.showInputBox({ prompt: 'Tag this release with: ', ignoreFocusOut: true }).then(function(tag) { 54 | vscode.window.showInputBox({ prompt: 'Would you like this release branch deleted after finishing? (y/n)', ignoreFocusOut: true }).then(function(val) { 55 | if(val !== undefined && (val.toLowerCase() === 'y' || val.toLowerCase() === 'n')) { 56 | var deleteBranch = val.toLowerCase() === 'y'; 57 | finishRelease(outChannel, tag, deleteBranch); 58 | } 59 | }); 60 | }); 61 | else 62 | vscode.window.showInputBox({ prompt: 'Tag this release with: ', ignoreFocusOut: true }).then(tag => finishRelease(outChannel, tag, deleteByDefault)); 63 | } 64 | } 65 | 66 | function startRelease(outChannel, releaseName, baseBranch) { 67 | if(releaseName !== undefined) // User chose to Cancel/Esc operation 68 | if(releaseName !== '') { 69 | releaseName = releaseName.trim().replace(/ /g, '_'); 70 | gitUtils.getGitRepositoryPath(vscode.workspace.rootPath).then(function (gitRepositoryPath) { 71 | gitflowUtils.startRelease(gitRepositoryPath, releaseName, baseBranch) 72 | .then(startRelease, genericErrorHandler) 73 | .catch(genericErrorHandler) 74 | }).catch(genericErrorHandler); 75 | } 76 | else 77 | genericErrorHandler('Name of release cannot be blank'); 78 | 79 | function startRelease(log) { 80 | if(log.length === 0) { 81 | vscode.window.showInformationMessage('Nothing to show'); 82 | return; 83 | } 84 | 85 | let releasesConfig = config.get('gitflow4code.releases') as BranchSetting[]; 86 | releasesConfig.push(new BranchSetting(initValues.releases + releaseName, baseBranch)); 87 | config.update('gitflow4code.releases', releasesConfig); 88 | 89 | outChannel.append(log); 90 | outChannel.show(); 91 | } 92 | function genericErrorHandler(error) { 93 | if(error.code && error.syscall && error.code === 'ENOENT' && error.syscall === 'spawn git') 94 | vscode.window.showErrorMessage('Cannot find git installation'); 95 | else { 96 | outChannel.appendLine(error); 97 | outChannel.show(); 98 | vscode.window.showErrorMessage('There was an error, please view details in output log'); 99 | } 100 | } 101 | } 102 | 103 | function finishRelease(outChannel, releaseTag, deleteBranch) { 104 | gitUtils.getGitRepositoryPath(vscode.workspace.rootPath).then(function (gitRepositoryPath) { 105 | gitUtils.getCurrentBranchName(vscode.workspace.rootPath).then((branchName) => { 106 | let releasesConfig = config.get('gitflow4code.releases') as BranchSetting[]; 107 | let releaseSetting = releasesConfig.find((release) => release.name === branchName.toString()); 108 | if(!releaseSetting) 109 | releaseSetting = new BranchSetting(branchName.toString(), initValues.develop); 110 | 111 | gitflowUtils.finishRelease(gitRepositoryPath, releaseSetting.base, releaseTag, deleteBranch).then(finishRelease, genericErrorHandler); 112 | function finishRelease(log) { 113 | if(log.length === 0) { 114 | vscode.window.showInformationMessage('Nothing to show'); 115 | return; 116 | } 117 | 118 | if(deleteBranch) { 119 | let releaseIndex = releasesConfig.indexOf(releaseSetting); 120 | releasesConfig.splice(releaseIndex, 1); 121 | config.update('gitflow4code.releases', releasesConfig); 122 | } 123 | 124 | outChannel.append(log); 125 | outChannel.show(); 126 | } 127 | }) 128 | }).catch(genericErrorHandler); 129 | 130 | function genericErrorHandler(error) { 131 | if(error.code && error.syscall && error.code === 'ENOENT' && error.syscall === 'spawn git') 132 | vscode.window.showErrorMessage('Cannot find git installation'); 133 | else { 134 | outChannel.appendLine(error); 135 | outChannel.show(); 136 | vscode.window.showErrorMessage('There was an error, please view details in output log'); 137 | } 138 | } 139 | } -------------------------------------------------------------------------------- /src/commands/features.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { workspace } from 'vscode'; 3 | import * as gitflowUtils from '../helpers/gitflowUtils'; 4 | import * as gitUtils from '../helpers/gitUtils'; 5 | import * as path from 'path'; 6 | import { InitConfigSettings } from '../settings/configSettings'; 7 | import { BranchSetting } from '../settings/branchSettings'; 8 | 9 | const config = workspace.getConfiguration(); 10 | const initValues = config.get('gitflow4code.init') as InitConfigSettings; 11 | const askForDeletion = config.get('gitflow4code.askBeforeDeletion') as boolean; 12 | const deleteByDefault = config.get('gitflow4code.deleteBranchByDefault') as boolean; 13 | const pushAfterFinishing = config.get('gitflow4code.pushAfterFinishing') as boolean; 14 | 15 | export function run(outChannel, action) { 16 | if(action === 'start') { 17 | var itemPickList = [ 18 | { 19 | label: 'Start Feature from ' + initValues.develop, 20 | description: '' 21 | }, 22 | { 23 | label: 'Start Feature from another base branch', 24 | description: '' 25 | } 26 | ]; 27 | 28 | vscode.window.showQuickPick(itemPickList).then(function(item) { 29 | if(!item) return; 30 | 31 | outChannel.clear(); 32 | if(item.label === itemPickList[0].label) 33 | vscode.window.showInputBox({ prompt: 'Name of Feature: ', ignoreFocusOut: true }).then(val => startFeature(outChannel, val, initValues.develop)); 34 | else { 35 | gitUtils.getBranchList(workspace.rootPath).then((features) => { 36 | var branchPickList = []; 37 | features.forEach(branchName => { 38 | branchPickList.push( { label: branchName, description: 'create feature branch using ' + branchName + ' as your base'}); 39 | }); 40 | 41 | vscode.window.showQuickPick(branchPickList).then(function(item) { 42 | if(!item) return; 43 | 44 | outChannel.clear(); 45 | vscode.window.showInputBox({ prompt: 'Name of Feature: ', ignoreFocusOut: true }).then(val => startFeature(outChannel, val, item.label)); 46 | }); 47 | }); 48 | } 49 | }); 50 | } 51 | else if (action === 'finish') { 52 | if(askForDeletion) 53 | vscode.window.showInputBox({ prompt: 'Would you like this feature branch deleted after finishing? (y/n)', ignoreFocusOut: true }).then(function(val) { 54 | if(val !== undefined && (val.toLowerCase() === 'y' || val.toLowerCase() === 'n')) { 55 | var deleteBranch = val.toLowerCase() === 'y'; 56 | finishFeature(outChannel, deleteBranch); 57 | } 58 | }); 59 | else 60 | finishFeature(outChannel, deleteByDefault); 61 | } 62 | } 63 | 64 | function startFeature(outChannel, featureName, baseBranch) { 65 | if(featureName !== undefined) // User chose to Cancel/Esc operation 66 | if(featureName !== '') { 67 | featureName = featureName.trim().replace(/ /g, '_'); 68 | gitUtils.getGitRepositoryPath(vscode.workspace.rootPath).then(function (gitRepositoryPath) { 69 | gitflowUtils.startFeature(gitRepositoryPath, featureName, baseBranch) 70 | .then(startFeature, genericErrorHandler) 71 | .catch(genericErrorHandler) 72 | }).catch(genericErrorHandler); 73 | } 74 | else 75 | genericErrorHandler('Name of feature cannot be blank'); 76 | 77 | function startFeature(log) { 78 | if(log.length === 0) { 79 | vscode.window.showInformationMessage('Nothing to show'); 80 | return; 81 | } 82 | 83 | let featuresConfig = config.get('gitflow4code.features') as BranchSetting[]; 84 | featuresConfig.push(new BranchSetting(initValues.features + featureName, baseBranch, pushAfterFinishing)); 85 | config.update('gitflow4code.features', featuresConfig); 86 | 87 | outChannel.append(log); 88 | outChannel.show(); 89 | } 90 | function genericErrorHandler(error) { 91 | if(error.code && error.syscall && error.code === 'ENOENT' && error.syscall === 'spawn git') 92 | vscode.window.showErrorMessage('Cannot find git installation'); 93 | else { 94 | outChannel.appendLine(error); 95 | outChannel.show(); 96 | vscode.window.showErrorMessage('There was an error, please view details in output log'); 97 | } 98 | } 99 | } 100 | 101 | function finishFeature(outChannel, deleteBranch) { 102 | gitUtils.getGitRepositoryPath(vscode.workspace.rootPath).then(function (gitRepositoryPath) { 103 | gitUtils.getCurrentBranchName(vscode.workspace.rootPath).then((branchName) => { 104 | if(branchName.toString().startsWith(initValues.features)) { 105 | let featuresConfig = config.get('gitflow4code.features') as BranchSetting[]; 106 | let featureSetting = featuresConfig.find((feature) => feature.name === branchName.toString()); 107 | if(!featureSetting) 108 | featureSetting = new BranchSetting(branchName.toString(), initValues.develop, pushAfterFinishing); 109 | 110 | let options = { 111 | pushToOrigin: pushAfterFinishing 112 | }; 113 | gitflowUtils.finishFeature(gitRepositoryPath, branchName.toString(), featureSetting.base, deleteBranch, options).then(finishFeature, genericErrorHandler); 114 | function finishFeature(log) { 115 | if(log.length === 0) { 116 | vscode.window.showInformationMessage('Nothing to show'); 117 | return; 118 | } 119 | 120 | if(deleteBranch) { 121 | let featureIndex = featuresConfig.indexOf(featureSetting); 122 | featuresConfig.splice(featureIndex, 1); 123 | config.update('gitflow4code.features', featuresConfig); 124 | } 125 | 126 | outChannel.append(log); 127 | outChannel.show(); 128 | } 129 | 130 | } 131 | else 132 | vscode.window.showErrorMessage('Not currently on a Feature branch'); 133 | }) 134 | }).catch(genericErrorHandler); 135 | 136 | function genericErrorHandler(error) { 137 | if(error.code && error.syscall && error.code === 'ENOENT' && error.syscall === 'spawn git') 138 | vscode.window.showErrorMessage('Cannot find git installation'); 139 | else { 140 | outChannel.appendLine(error); 141 | outChannel.show(); 142 | vscode.window.showErrorMessage('There was an error, please view details in output log'); 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gitflow4code 2 | ## gitflow-avh Implementation for Visual Studio Code 3 | This extension adds support for the feature branching strategy described here in [Vincent Driessen's branching model](http://nvie.com/posts/a-successful-git-branching-model/). The implementation used by this extension is similar to the functionality offered by [git-flow (AVH Edition)](https://github.com/petervanderdoes/gitflow-avh). 4 | 5 | ### What's included 6 | * Initialize a repository with default or custom values for branch naming (i.e. 'develop', 'master', 'feature/', etc) 7 | * Start/Finish Feature branches from "development", or from other branch bases 8 | * Start/Finish Release branches from "development", or from other branch bases 9 | * Start/Finish Hotfix branches from "production", or from other branch bases 10 | * Get the status of a git repository 11 | 12 | #### Dependencies 13 | * git (if you have installed VS Code, this should also already exist on your machine) 14 | 15 | # Getting Started 16 | ## Accessing gitflow4code commands 17 | From the Command Pallette, type in GitFlow to filter the GitFlow commands 18 | 19 | ![GitFlow Command](images/FinishFeature.png) 20 | 21 | ## Initialize 22 | Choose the Initialize Repository command from the list of available gitflow4code commands. 23 | 24 | ![Available GitFlow Commands](images/Initialize-Command.png) 25 | 26 | Alternately, you can use the shortcut keys listed below 27 | ##### Shortcuts to Initialize Command 28 | * ⌥⌘/ i on Mac 29 | * Ctrl+Alt+/ i on Windows 30 | 31 | ### Initialize with default settings 32 | Choose to `Initialize with default` (i.e. `master`, `develop`, `feature/`, `release/`, & `hotfix/`) 33 | 34 | ![Initialize with default](images/InitializeWithDefault.png) 35 | 36 | ### Initialize with custom configuration settings 37 | Choose to `Initialize with custom values` and then specify the values requested 38 | 39 | ![Initialize master](images/MasterBranch.png) 40 | ![Initialize develop](images/DevelopBranch.png) 41 | ![Initialize feature](images/FeatureBranch.png) 42 | ![Initialize release](images/ReleaseBranch.png) 43 | ![Initialize hotfix](images/HotfixBranch.png) 44 | 45 | These settings are stored and can be edited at any time by re-running the above commands or by editing in the Settings file. 46 | 47 | ![custom settings](images/InitializeSettings.png) 48 | 49 | 50 | ## Features 51 | ### Start Feature 52 | Choose the `Start Feature` command from the list of available gitflow4code commands 53 | 54 | ![Features Command](images/StartFeature-Command.png) 55 | 56 | Alternately, you can use the shortcut keys listed below 57 | ##### Shortcuts to Start Feature Command 58 | * ⌥⌘/ f on Mac 59 | * Ctrl+Alt+/ f on Windows 60 | 61 | If creating a feature branch off of your development branch, choose `Start Feature from ` (where `develop` is the name of whatever you chose to initialize your development branch) 62 | 63 | ![Start Feature](images/StartFeatureDevelop.png) 64 | 65 | -- or -- 66 | 67 | If creating a feature branch off another base branch, choose `Start Feature from another base branch` 68 | 69 | ![Start Feature from another base branch](images/StartFeatureBase.png) 70 | 71 | 72 | Then supply the name of your feature branch, and the tool will prefix it with whatever you chose to be the name of your feature branches 73 | 74 | ![Feature Name](images/FeatureName.png) 75 | 76 | ### Finish Feature 77 | When finished with your feature, choose the `Features` command from the list of available gitflow4code commands and then choose to `Finish Feature` 78 | 79 | ![Finish Feature](images/FinishFeature.png) 80 | 81 | Alternately, you can use the shortcut keys listed below 82 | ##### Shortcuts to Finish Feature Command 83 | * ⌥⌘/ ⌘f on Mac 84 | * Ctrl+Alt+/ Ctrl+f on Windows 85 | 86 | Or, use the Finish Feature button in the status bar 87 | 88 | ![Finish Feature](images/FinishFeature-Statusbar.png) 89 | 90 | ## Releases 91 | ### Start Release 92 | Choose the `Releases` command from the list of available gitflow4code commands 93 | 94 | ![Releases Command](images/StartRelease-Command.png) 95 | 96 | Alternately, you can use the shortcut keys listed below 97 | ##### Shortcuts to Start Release Command 98 | * ⌥⌘/ r on Mac 99 | * Ctrl+Alt+/ r on Windows 100 | 101 | If creating a release branch off of your development branch, choose `Start Release from ` (where `develop` is the name of whatever you chose to initialize your development branch) 102 | 103 | ![Start Release](images/StartReleaseDevelop.png) 104 | 105 | -- or -- 106 | 107 | If creating a release branch off another base branch, choose `Start Release from another base branch` 108 | 109 | ![Start Release from another base branch](images/StartReleaseBase.png) 110 | 111 | Then supply the name of your release branch, and the tool will prefix it with whatever you chose to be the name of your release branches 112 | 113 | ![Release Name](images/ReleaseName.png) 114 | 115 | ### Finish Release 116 | When finished with your release, choose the `Releases` command from the list of available gitflow4code commands and then choose to `Finish Release` 117 | 118 | ![Finish Release](images/FinishRelease.png) 119 | 120 | Alternately, you can use the shortcut keys listed below 121 | ##### Shortcuts to Finish Release Command 122 | * ⌥⌘/ ⌘r on Mac 123 | * Ctrl+Alt+/ Ctrl+r on Windows 124 | 125 | Or, use the Finish Release button in the status bar 126 | 127 | ![Finish Release](images/FinishRelease-Statusbar.png) 128 | 129 | Then supply the name of the `Tag` for this release 130 | 131 | ![Tag Release](images/TagRelease.png) 132 | 133 | ## Hotfixes 134 | ### Start Hotfix 135 | Choose the `Hotfixes` command from the list of available gitflow4code commands 136 | 137 | ![Hotfixes Command](images/StartHotfix-Command.png) 138 | 139 | Alternately, you can use the shortcut keys listed below 140 | ##### Shortcuts to Start Hotfix Command 141 | * ⌥⌘/ h on Mac 142 | * Ctrl+Alt+/ h on Windows 143 | 144 | If creating a hotfix branch off of your production branch, choose `Start Hotfix from ` (where `master` is the name of whatever you chose to initialize your production branch) 145 | 146 | ![Start Hotfix](images/StartHotfixMaster.png) 147 | 148 | -- or -- 149 | 150 | If creating a hotfix branch off another base branch, choose `Start Hotfix from another base branch` 151 | 152 | ![Start Hotfix from another base branch](images/StartHotfixBase.png) 153 | 154 | Then supply the name of your hotfix branch, and the tool will prefix it with whatever you chose to be the name of your hotfix branches 155 | 156 | ![Hotfix Name](images/HotfixName.png) 157 | 158 | ### Finish Hotfix 159 | When finished with your hotfix, choose the `Hotfixes` command from the list of available gitflow4code commands and then choose to `Finish Hotfix` 160 | 161 | ![Finish Hotfix](images/FinishHotfix.png) 162 | 163 | Alternately, you can use the shortcut keys listed below 164 | ##### Shortcuts to Finish Hotfix Command 165 | * ⌥⌘/ ⌘h on Mac 166 | * Ctrl+Alt+/ Ctrl+h on Windows 167 | 168 | Or, use the Finish Hotfix button in the status bar 169 | 170 | ![Finish Hotfix](images/FinishHotfix-Statusbar.png) 171 | 172 | Then supply the name of the `Tag` for this hotfix 173 | 174 | ![Tag Hotfix](images/TagHotfix.png) 175 | 176 | 177 | ## Git Status 178 | Choosing this from the gitflow4code commands list will display the current status of the local git repository (same as if running `git status` on the command line) and displays it in the `OUTPUT` pane 179 | 180 | ![Git Status](images/GetStatus-Command.png) 181 | 182 | Alternately, you can use the shortcut keys listed below 183 | ##### Shortcuts to Start Hotfix Command 184 | * ⌥⌘/ s on Mac 185 | * Ctrl+Alt+/ s on Windows 186 | 187 | ![Git Status Result](images/GetStatus-Result.png) 188 | 189 | 190 | # gitflow4code roadmap 191 | See our [enhancements list](https://github.com/Shaggy13spe/gitflow4code/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement) for future plans 192 | 193 | 194 | ## Help Out? 195 | This project is under constant development. Feedback and suggestions are very welcome and I encourage you to use the [Issues](https://github.com/Shaggy13spe/gitflow4code/issues) list on Github to provide that feedback. 196 | 197 | ### Contributing 198 | Fork the repository and then run: 199 | ```sh 200 | $ git clone -b master git@github.com:/gitflow4code.git 201 | cd gitflow4code 202 | ``` 203 | 204 | The `-b master` switch has to be added since the fork operation automatically clones the `develop` branch of the repository and cloning it results in a local repository with just a `develop` branch. 205 | 206 | After that, initialize the local gitflow4code repository with `gitflow4code` itself (see Getting Started above). 207 | Then create a feature branch, do your work and commit your changes publishing your feature branch. 208 | 209 | When done, open a pull request to your feature branch. 210 | 211 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gitflow4code", 3 | "displayName": "GitFlow 4 Code", 4 | "description": "Adds GitFlow support to VS Code", 5 | "icon": "images/gitflow.jpg", 6 | "version": "1.2.3", 7 | "publisher": "GreatMinds", 8 | "license": "SEE LICENSE IN LICENSE or README.MD", 9 | "homepage": "https://github.com/shaggy13spe/gitflow4code/blob/master/README.md", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/shaggy13spe/gitflow4code/" 13 | }, 14 | "categories": [ 15 | "Other" 16 | ], 17 | "keywords": [ 18 | "git", 19 | "gitflow", 20 | "source control", 21 | "branching" 22 | ], 23 | "bugs": { 24 | "url": "https://github.com/shaggy13spe/gitflow4code/issues" 25 | }, 26 | "galleryBanner": { 27 | "color": "#0000FF", 28 | "theme": "dark" 29 | }, 30 | "engines": { 31 | "vscode": "^1.21.0" 32 | }, 33 | "tags": [ 34 | "git", 35 | "gitflow" 36 | ], 37 | "activationEvents": [ 38 | "*" 39 | ], 40 | "main": "./out/src/gitflow", 41 | "contributes": { 42 | "commands": [ 43 | { 44 | "command": "gitflow.initialize", 45 | "title": "Initialize GitFlow", 46 | "category": "GitFlow" 47 | }, 48 | { 49 | "command": "gitflow.startFeature", 50 | "title": "Start a Feature", 51 | "category": "GitFlow" 52 | }, 53 | { 54 | "command": "gitflow.finishFeature", 55 | "title": "Finish Feature", 56 | "category": "GitFlow" 57 | }, 58 | { 59 | "command": "gitflow.startRelease", 60 | "title": "Start a Release", 61 | "category": "GitFlow" 62 | }, 63 | { 64 | "command": "gitflow.finishRelease", 65 | "title": "Finish Release", 66 | "category": "GitFlow" 67 | }, 68 | { 69 | "command": "gitflow.startHotfix", 70 | "title": "Start a Hotfix", 71 | "category": "GitFlow" 72 | }, 73 | { 74 | "command": "gitflow.finishHotfix", 75 | "title": "Finish Hotfix", 76 | "category": "GitFlow" 77 | }, 78 | { 79 | "command": "gitflow.gitStatus", 80 | "title": "Git Status", 81 | "category": "GitFlow" 82 | } 83 | ], 84 | "keybindings": [ 85 | { 86 | "key": "alt+ctrl+/ f", 87 | "mac": "alt+cmd+/ f", 88 | "command": "gitflow.startFeature" 89 | }, 90 | { 91 | "key": "alt+ctrl+/ ctrl+f", 92 | "mac": "alt+cmd+/ cmd+f", 93 | "command": "gitflow.finishFeature" 94 | }, 95 | { 96 | "key": "alt+ctrl+/ r", 97 | "mac": "alt+cmd+/ r", 98 | "command": "gitflow.startRelease" 99 | }, 100 | { 101 | "key": "alt+ctrl+/ ctrl+r", 102 | "mac": "alt+cmd+/ cmd+r", 103 | "command": "gitflow.finishRelease" 104 | }, 105 | { 106 | "key": "alt+ctrl+/ h", 107 | "mac": "alt+cmd+/ h", 108 | "command": "gitflow.startHotfix" 109 | }, 110 | { 111 | "key": "alt+ctrl+/ ctrl+h", 112 | "mac": "alt+cmd+/ cmd+h", 113 | "command": "gitflow.finishHotfix" 114 | }, 115 | { 116 | "key": "alt+ctrl+/ i", 117 | "mac": "alt+cmd+/ i", 118 | "command": "gitflow.initialize" 119 | }, 120 | { 121 | "key": "alt+ctrl+/ s", 122 | "mac": "alt+cmd+/ s", 123 | "command": "gitflow.gitStatus" 124 | } 125 | ], 126 | "configuration": { 127 | "type": "object", 128 | "title": "greatminds.gitflow4code", 129 | "properties": { 130 | "gitflow4code.showStatusBarFinisher": { 131 | "type": "boolean", 132 | "description": "Controls whether or not the 'Finish' status bar button is displayed", 133 | "default": true 134 | }, 135 | "gitflow4code.askBeforeDeletion": { 136 | "type": "boolean", 137 | "description": "Controls whether user is asked to confirm deletion of feature, release, and/or hotfix branches after finishing them", 138 | "default": true 139 | }, 140 | "gitflow4code.deleteBranchByDefault": { 141 | "type": "boolean", 142 | "description": "Controls deletion of feature, release, and/or hotfix branches after finishing them", 143 | "default": false 144 | }, 145 | "gitflow4code.pushAfterFinishing": { 146 | "type": "boolean", 147 | "description": "Default global setting, controls whether base branches are pushed to origin (if exist) after feature branches are finished and merged to them", 148 | "default": false 149 | }, 150 | "gitflow4code.init": { 151 | "type": "object", 152 | "description": "Initialization values for git flow branches", 153 | "default": { 154 | "develop": "develop", 155 | "master": "master", 156 | "features": "feature/", 157 | "releases": "release/", 158 | "hotfixes": "hotfix/" 159 | } 160 | }, 161 | "gitflow4code.features": { 162 | "type": "array", 163 | "items": { 164 | "type": "object", 165 | "title": "feature/base pairs", 166 | "properties": { 167 | "name": { 168 | "type": "string", 169 | "description": "Name of feature branch" 170 | }, 171 | "base": { 172 | "type": "string", 173 | "description": "Name of branch feature is based from" 174 | }, 175 | "pushAfterFinishing": { 176 | "type": "boolean", 177 | "description": "Controls if base branches are pushed to origin after finishing feature branch, default is set via the global default setting, but can be controlled individually as well", 178 | "default": false 179 | } 180 | } 181 | }, 182 | "description": "maps base branch to features", 183 | "default": [] 184 | }, 185 | "gitflow4code.releases": { 186 | "type": "array", 187 | "items": { 188 | "type": "object", 189 | "title": "release/base pairs", 190 | "properties": { 191 | "name": { 192 | "type": "string", 193 | "description": "Name of release branch" 194 | }, 195 | "base": { 196 | "type": "string", 197 | "description": "Name of branch release is based from" 198 | } 199 | } 200 | }, 201 | "description": "maps base branch to releases", 202 | "default": [] 203 | }, 204 | "gitflow4code.hotfixes": { 205 | "type": "array", 206 | "items": { 207 | "type": "object", 208 | "title": "hotfix/base pairs", 209 | "properties": { 210 | "name": { 211 | "type": "string", 212 | "description": "Name of hotfix branch" 213 | }, 214 | "base": { 215 | "type": "string", 216 | "description": "Name of branch hotfix is based from" 217 | } 218 | } 219 | }, 220 | "description": "maps base branch to hotfixes", 221 | "default": [] 222 | } 223 | } 224 | } 225 | }, 226 | "badges": [ 227 | { 228 | "url": "https://vsmarketplacebadge.apphb.com/version-short/GreatMinds.gitflow4code.svg", 229 | "href": "https://marketplace.visualstudio.com/items?itemName=GreatMinds.gitflow4code", 230 | "description": "Extension version" 231 | }, 232 | { 233 | "url": "https://vsmarketplacebadge.apphb.com/installs/GreatMinds.gitflow4code.svg", 234 | "href": "https://marketplace.visualstudio.com/items?itemName=GreatMinds.gitflow4code", 235 | "description": "Extension installs" 236 | }, 237 | { 238 | "url": "https://vsmarketplacebadge.apphb.com/rating/GreatMinds.gitflow4code.svg", 239 | "href": "https://marketplace.visualstudio.com/items?itemName=GreatMinds.gitflow4code#review-details", 240 | "description": "Extension rating" 241 | } 242 | ], 243 | "scripts": { 244 | "vscode:prepublish": "tsc -p ./", 245 | "compile": "tsc -watch -p ./", 246 | "postinstall": "node ./node_modules/vscode/bin/install", 247 | "test": "node ./node_modules/vscode/bin/test" 248 | }, 249 | "devDependencies": { 250 | "@types/mocha": "^2.2.32", 251 | "@types/node": "^6.0.40", 252 | "mocha": "^2.3.3", 253 | "typescript": "^2.0.3", 254 | "vscode": "^1.1.14" 255 | }, 256 | "extensionDependencies": [ 257 | "vscode.git" 258 | ] 259 | } 260 | -------------------------------------------------------------------------------- /src/helpers/gitflowUtils.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import * as vscode from 'vscode'; 3 | import { workspace, extensions } from 'vscode'; 4 | import * as path from 'path'; 5 | import * as child_process_1 from 'child_process'; 6 | import * as fs from 'fs'; 7 | import * as gitUtils from '../helpers/gitUtils'; 8 | import { InitConfigSettings } from '../settings/configSettings'; 9 | import { BranchSetting } from '../settings/branchSettings'; 10 | 11 | const api = extensions.getExtension('vscode.git').exports; 12 | const config = workspace.getConfiguration(); 13 | const initValues = config.get('gitflow4code.init') as InitConfigSettings; 14 | 15 | 16 | /* 17 | A branch name can not: 18 | - Have a path component that begins with "." 19 | - Have a double dot ".." 20 | - Have an ASCII control character, "~", "^", ":" or SP, anywhere 21 | - End with a "/" 22 | - End with ".lock" 23 | - Contain a "\" (backslash) 24 | */ 25 | 26 | function hasIllegalChars(branchName) { 27 | 28 | if(branchName.indexOf('/') > 0) { 29 | var index = branchName.indexOf('/'); 30 | if(index === branchName.length - 1) 31 | return true; 32 | } 33 | else if(branchName.indexOf('.lock') >= 0) { 34 | var index = branchName.indexOf('.lock'); 35 | if(index === branchName.length - 5) 36 | return true; 37 | } 38 | else if(branchName.indexOf('/.') >= 0) 39 | return true; 40 | else if(branchName.indexOf('..') >= 0) 41 | return true; 42 | else if(branchName.indexOf('~') >= 0) 43 | return true; 44 | else if(branchName.indexOf('^') >= 0) 45 | return true; 46 | else if(branchName.indexOf(':') >= 0) 47 | return true; 48 | else if(branchName.indexOf('\\') >= 0) 49 | return true; 50 | 51 | else return false; 52 | } 53 | 54 | export function initializeRepository(rootDir) { 55 | return api.getGitPath().then(function(gitExecutable) { 56 | 57 | const config = workspace.getConfiguration(); 58 | const configValues = config.get('gitflow4code.init') as InitConfigSettings; 59 | return new Promise(function(resolve, reject) { 60 | let log = ''; 61 | let error = ''; 62 | gitUtils.checkForBranch(rootDir, configValues.master).then(function(result) { 63 | if(!result) { 64 | let options = { cwd: rootDir }; 65 | let spawn = require('child_process').spawn; 66 | let ls = spawn(gitExecutable, ['branch', configValues.master], options); 67 | ls.stdout.on('data', function(data) { 68 | log += data + '\n'; 69 | }); 70 | ls.stderr.on('data', function(data) { 71 | error += data + '\n'; 72 | }); 73 | ls.on('exit', function(code) { 74 | if(code > 0) return; 75 | 76 | log += 'Created production branch ' + configValues.master + '\n\n'; 77 | }); 78 | } 79 | gitUtils.checkForBranch(rootDir, configValues.develop).then(function(result) { 80 | if(!result) { 81 | let options = { cwd: rootDir }; 82 | let spawn = require('child_process').spawn; 83 | let ls = spawn(gitExecutable, ['checkout', '-b', configValues.develop], options); 84 | ls.stdout.on('data', function(data) { 85 | log += data + '\n'; 86 | }); 87 | ls.stderr.on('data', function(data) { 88 | error += data + '\n'; 89 | }); 90 | ls.on('exit', function(code) { 91 | if(code > 0) { 92 | reject(error); 93 | return; 94 | } 95 | var message = log; 96 | if(code === 0 && error.length > 0) 97 | message += '\n\n' + error; 98 | 99 | resolve(message); 100 | }); 101 | } 102 | else 103 | resolve('Repository already initialized'); 104 | }); 105 | }); 106 | 107 | }); 108 | 109 | 110 | 111 | }); 112 | } 113 | 114 | export function startFeature(rootDir, featureName, baseBranch) { 115 | return api.getGitPath().then(function (gitExecutable) { 116 | return new Promise(function (resolve, reject) { 117 | const config = workspace.getConfiguration(); 118 | const configValues = config.get('gitflow4code.init') as InitConfigSettings; 119 | const featurePrefix = configValues.features; 120 | 121 | if(hasIllegalChars(featureName)) 122 | reject('Branch name has illegal characters\n' + 123 | 'A branch name can not:\n' + 124 | '\t- Have a path component that begins with "."\n' + 125 | '\t- Have a double dot ".."\n' + 126 | '\t- Have an ASCII control character, "~", "^", ":" or SP, anywhere\n' + 127 | '\t- End with a "/"\n' + 128 | '\t- End with ".lock"\n' + 129 | '\t- Contain a "\" (backslash))'); 130 | else { 131 | if(!baseBranch) 132 | baseBranch = configValues.develop; 133 | 134 | let options = { cwd: rootDir }; 135 | let spawn = require('child_process').spawn; 136 | let ls = spawn(gitExecutable, ['checkout', '-b', featurePrefix + featureName, baseBranch], options); 137 | let log = ''; 138 | let error = ''; 139 | ls.stdout.on('data', function (data) { 140 | log += data + '\n'; 141 | }); 142 | ls.stderr.on('data', function (data) { 143 | error += data; 144 | }); 145 | ls.on('exit', function (code) { 146 | if(code > 0) { 147 | reject(error); 148 | return; 149 | } 150 | var message = log; 151 | if(code === 0 && error.length > 0) 152 | message += '\n\n' + error; 153 | 154 | resolve(message); 155 | }); 156 | } 157 | 158 | 159 | }); 160 | }); 161 | } 162 | 163 | export function finishFeature(rootDir, featureName, baseBranch, deleteBranch, options) { 164 | return api.getGitPath().then(function (gitExecutable) { 165 | return new Promise(function(resolve, reject) { 166 | let gitOptions = { cwd: rootDir }; 167 | let spawn = require('child_process').spawn; 168 | let log = ''; 169 | let error = ''; 170 | let ls1 = spawn(gitExecutable, ['checkout', baseBranch], gitOptions); 171 | ls1.stdout.on('data', function (data) { 172 | log += data + '\n'; 173 | }); 174 | ls1.stderr.on('data', function (data) { 175 | error += data; 176 | }); 177 | ls1.on('exit', function (code) { 178 | if(code > 0) { 179 | reject(error); 180 | return; 181 | } 182 | 183 | let ls2 = spawn(gitExecutable, ['merge', '--no-ff', featureName], gitOptions); 184 | ls2.stdout.on('data', function (data) { 185 | log += data + '\n'; 186 | }); 187 | ls2.stderr.on('data', function (data) { 188 | error += data; 189 | }); 190 | ls2.on('exit', function (code) { 191 | if(code > 0) { 192 | reject(error); 193 | return; 194 | } 195 | if(deleteBranch) { 196 | let ls3 = spawn(gitExecutable, ['branch', '-d', featureName], gitOptions); 197 | ls3.stdout.on('data', function (data) { 198 | log += data + '\n'; 199 | }); 200 | ls3.stderr.on('data', function (data) { 201 | error += data; 202 | }); 203 | ls3.on('exit', function (code) { 204 | if(code > 0) { 205 | reject(error); 206 | return; 207 | } 208 | if(options.pushToOrigin) { 209 | let ls4 = spawn(gitExecutable, ['push', 'origin', initValues.develop], gitOptions); 210 | ls4.stdout.on('data', function (data) { 211 | log += data + '\n'; 212 | }); 213 | ls4.stderr.on('data', function (data) { 214 | error += data; 215 | }); 216 | ls4.on('exit', function (code) { 217 | if(code > 0) { 218 | reject(error); 219 | return; 220 | } 221 | var message = log; 222 | if(code === 0 && error.length > 0) 223 | message += '\n\n' + error; 224 | 225 | resolve(message); 226 | }); 227 | } 228 | else { 229 | var message = log; 230 | if(code === 0 && error.length > 0) 231 | message += '\n\n' + error; 232 | 233 | resolve(message); 234 | } 235 | }); 236 | } 237 | else { 238 | if(options.pushToOrigin) { 239 | let ls3 = spawn(gitExecutable, ['push', 'origin', initValues.develop], gitOptions); 240 | ls3.stdout.on('data', function (data) { 241 | log += data + '\n'; 242 | }); 243 | ls3.stderr.on('data', function (data) { 244 | error += data; 245 | }); 246 | ls3.on('exit', function (code) { 247 | if(code > 0) { 248 | reject(error); 249 | return; 250 | } 251 | var message = log; 252 | if(code === 0 && error.length > 0) 253 | message += '\n\n' + error; 254 | 255 | resolve(message); 256 | }); 257 | } 258 | else { 259 | var message = log; 260 | if(code === 0 && error.length > 0) 261 | message += '\n\n' + error; 262 | 263 | resolve(message); 264 | } 265 | } 266 | }); 267 | }); 268 | }); 269 | }); 270 | } 271 | 272 | export function startRelease(rootDir, releaseName, baseBranch) { 273 | return api.getGitPath().then(function (gitExecutable) { 274 | return new Promise(function (resolve, reject) { 275 | const config = workspace.getConfiguration(); 276 | const configValues = config.get('gitflow4code.init') as InitConfigSettings; 277 | const releasePrefix = configValues.releases; 278 | 279 | if(hasIllegalChars(releaseName)) 280 | reject('Branch name has illegal characters\n' + 281 | 'A branch name can not:\n' + 282 | '\t- Have a path component that begins with "."\n' + 283 | '\t- Have a double dot ".."\n' + 284 | '\t- Have an ASCII control character, "~", "^", ":" or SP, anywhere\n' + 285 | '\t- End with a "/"\n' + 286 | '\t- End with ".lock"\n' + 287 | '\t- Contain a "\" (backslash))'); 288 | else { 289 | if(!baseBranch) 290 | baseBranch = configValues.develop; 291 | 292 | let options = { cwd: rootDir }; 293 | let spawn = require('child_process').spawn; 294 | let ls = spawn(gitExecutable, ['checkout', '-b', releasePrefix + releaseName, baseBranch], options); 295 | let log = ''; 296 | let error = ''; 297 | ls.stdout.on('data', function (data) { 298 | log += data + '\n'; 299 | }); 300 | ls.stderr.on('data', function (data) { 301 | error += data; 302 | }); 303 | ls.on('exit', function (code) { 304 | if(code > 0) { 305 | reject(error); 306 | return; 307 | } 308 | var message = log; 309 | if(code === 0 && error.length > 0) 310 | message += '\n\n' + error; 311 | 312 | resolve(message); 313 | }); 314 | } 315 | }); 316 | }); 317 | } 318 | 319 | export function finishRelease(rootDir, baseBranch, releaseTag, deleteBranch) { 320 | return api.getGitPath().then(function (gitExecutable) { 321 | return new Promise(function(resolve, reject) { 322 | let gitOptions = { cwd: rootDir }; 323 | let spawn = require('child_process').spawn; 324 | let ls1 = spawn(gitExecutable, ['rev-parse', '--abbrev-ref', 'HEAD'], gitOptions); 325 | var branchData = ''; 326 | var inRelease = false; 327 | var currentBranch = ''; 328 | let log = ''; 329 | let error = ''; 330 | ls1.stdout.on('data', function (data) { 331 | branchData += data; 332 | }); 333 | ls1.stderr.on('data', function (data) { 334 | error += data; 335 | }); 336 | ls1.on('exit', function(code) { 337 | if(code > 0) { 338 | reject(error); 339 | return; 340 | } 341 | 342 | currentBranch = branchData.replace(/ /g,'').trim().split('\n')[0]; 343 | const config = workspace.getConfiguration(); 344 | const configValues = config.get('gitflow4code.init') as InitConfigSettings; 345 | const releasePrefix = configValues.releases; 346 | inRelease = currentBranch.startsWith(releasePrefix); 347 | 348 | if(!inRelease) { 349 | reject('Not currently on a Release branch'); 350 | return; 351 | } 352 | let ls2 = spawn(gitExecutable, ['checkout', configValues.master], gitOptions); 353 | ls2.stdout.on('data', function (data) { 354 | log += data + '\n'; 355 | }); 356 | ls2.stderr.on('data', function (data) { 357 | error += data; 358 | }); 359 | ls2.on('exit', function (code) { 360 | if(code > 0) { 361 | reject(error); 362 | return; 363 | } 364 | 365 | let ls3 = spawn(gitExecutable, ['merge', '--no-ff', currentBranch], gitOptions); 366 | ls3.stdout.on('data', function (data) { 367 | log += data + '\n'; 368 | }); 369 | ls3.stderr.on('data', function (data) { 370 | error += data; 371 | }); 372 | ls3.on('exit', function (code) { 373 | if(code > 0) { 374 | reject(error); 375 | return; 376 | } 377 | 378 | let ls4 = spawn(gitExecutable, ['tag', '-a', releaseTag], gitOptions); 379 | ls4.stdout.on('data', function (data) { 380 | log += data + '\n'; 381 | }); 382 | ls4.stderr.on('data', function (data) { 383 | error += data; 384 | }); 385 | ls4.on('exit', function (code) { 386 | if(code > 0) { 387 | reject(error); 388 | return; 389 | } 390 | 391 | let ls5 = spawn(gitExecutable, ['checkout', baseBranch], gitOptions); 392 | ls5.stdout.on('data', function (data) { 393 | log += data + '\n'; 394 | }); 395 | ls5.stderr.on('data', function (data) { 396 | error += data; 397 | }); 398 | ls5.on('exit', function (code) { 399 | if(code > 0) { 400 | reject(error); 401 | return; 402 | } 403 | 404 | let ls6 = spawn(gitExecutable, ['merge', '--no-ff', currentBranch], gitOptions); 405 | ls6.stdout.on('data', function (data) { 406 | log += data + '\n'; 407 | }); 408 | ls6.stderr.on('data', function (data) { 409 | error += data; 410 | }); 411 | ls6.on('exit', function (code) { 412 | if(code > 0) { 413 | reject(error); 414 | return; 415 | } 416 | if(deleteBranch) { 417 | let ls7 = spawn(gitExecutable, ['branch', '-d', currentBranch], gitOptions); 418 | ls7.stdout.on('data', function (data) { 419 | log += data + '\n'; 420 | }); 421 | ls7.stderr.on('data', function (data) { 422 | error += data; 423 | }); 424 | ls7.on('exit', function (code) { 425 | if(code > 0) { 426 | reject(error); 427 | return; 428 | } 429 | var message = log; 430 | if(code === 0 && error.length > 0) 431 | message += '\n\n' + error; 432 | 433 | resolve(message); 434 | }); 435 | } 436 | else { 437 | var message = log; 438 | if(code === 0 && error.length > 0) 439 | message += '\n\n' + error; 440 | 441 | resolve(message); 442 | } 443 | }); 444 | }); 445 | }); 446 | }); 447 | }); 448 | }); 449 | }); 450 | }); 451 | } 452 | 453 | export function startHotfix(rootDir, hotfixName, baseBranch) { 454 | return api.getGitPath().then(function (gitExecutable) { 455 | return new Promise(function (resolve, reject) { 456 | const config = workspace.getConfiguration(); 457 | const configValues = config.get('gitflow4code.init') as InitConfigSettings; 458 | const hotfixPrefix = configValues.hotfixes; 459 | 460 | if(hasIllegalChars(hotfixName)) 461 | reject('Branch name has illegal characters\n' + 462 | 'A branch name can not:\n' + 463 | '\t- Have a path component that begins with "."\n' + 464 | '\t- Have a double dot ".."\n' + 465 | '\t- Have an ASCII control character, "~", "^", ":" or SP, anywhere\n' + 466 | '\t- End with a "/"\n' + 467 | '\t- End with ".lock"\n' + 468 | '\t- Contain a "\" (backslash))'); 469 | else { 470 | if(!baseBranch) 471 | baseBranch = configValues.master; 472 | 473 | let options = { cwd: rootDir }; 474 | let spawn = require('child_process').spawn; 475 | let ls = spawn(gitExecutable, ['checkout', '-b', hotfixPrefix + hotfixName, baseBranch], options); 476 | let log = ''; 477 | let error = ''; 478 | ls.stdout.on('data', function (data) { 479 | log += data + '\n'; 480 | }); 481 | ls.stderr.on('data', function (data) { 482 | error += data; 483 | }); 484 | ls.on('exit', function (code) { 485 | if(code > 0) { 486 | reject(error); 487 | return; 488 | } 489 | var message = log; 490 | if(code === 0 && error.length > 0) 491 | message += '\n\n' + error; 492 | 493 | resolve(message); 494 | }); 495 | } 496 | }); 497 | }); 498 | } 499 | 500 | export function finishHotfix(rootDir, baseBranch, hotfixTag, deleteBranch) { 501 | return api.getGitPath().then(function (gitExecutable) { 502 | return new Promise(function(resolve, reject) { 503 | let gitOptions = { cwd: rootDir }; 504 | let spawn = require('child_process').spawn; 505 | let ls1 = spawn(gitExecutable, ['rev-parse', '--abbrev-ref', 'HEAD'], gitOptions); 506 | var branchData = ''; 507 | var inHotfix = false; 508 | var currentBranch = ''; 509 | let log = ''; 510 | let error = ''; 511 | ls1.stdout.on('data', function (data) { 512 | branchData += data; 513 | }); 514 | ls1.stderr.on('data', function (data) { 515 | error += data; 516 | }); 517 | ls1.on('exit', function(code) { 518 | if(code > 0) { 519 | reject(error); 520 | return; 521 | } 522 | 523 | currentBranch = branchData.replace(/ /g,'').trim().split('\n')[0]; 524 | const config = workspace.getConfiguration(); 525 | const configValues = config.get('gitflow4code.init') as InitConfigSettings; 526 | const hotfixPrefix = configValues.hotfixes; 527 | inHotfix = currentBranch.startsWith(hotfixPrefix); 528 | 529 | if(!inHotfix) { 530 | reject('Not currently on a Hotfix branch'); 531 | return; 532 | } 533 | let ls2 = spawn(gitExecutable, ['checkout', baseBranch], gitOptions); 534 | ls2.stdout.on('data', function (data) { 535 | log += data + '\n'; 536 | }); 537 | ls2.stderr.on('data', function (data) { 538 | error += data; 539 | }); 540 | ls2.on('exit', function (code) { 541 | if(code > 0) { 542 | reject(error); 543 | return; 544 | } 545 | 546 | let ls3 = spawn(gitExecutable, ['merge', '--no-ff', currentBranch], gitOptions); 547 | ls3.stdout.on('data', function (data) { 548 | log += data + '\n'; 549 | }); 550 | ls3.stderr.on('data', function (data) { 551 | error += data; 552 | }); 553 | ls3.on('exit', function (code) { 554 | if(code > 0) { 555 | reject(error); 556 | return; 557 | } 558 | 559 | let ls4 = spawn(gitExecutable, ['tag', '-a', hotfixTag], gitOptions); 560 | ls4.stdout.on('data', function (data) { 561 | log += data + '\n'; 562 | }); 563 | ls4.stderr.on('data', function (data) { 564 | error += data; 565 | }); 566 | ls4.on('exit', function (code) { 567 | if(code > 0) { 568 | reject(error); 569 | return; 570 | } 571 | 572 | let ls5 = spawn(gitExecutable, ['checkout', configValues.develop], gitOptions); 573 | ls5.stdout.on('data', function (data) { 574 | log += data + '\n'; 575 | }); 576 | ls5.stderr.on('data', function (data) { 577 | error += data; 578 | }); 579 | ls5.on('exit', function (code) { 580 | if(code > 0) { 581 | reject(error); 582 | return; 583 | } 584 | 585 | let ls6 = spawn(gitExecutable, ['merge', '--no-ff', currentBranch], gitOptions); 586 | ls6.stdout.on('data', function (data) { 587 | log += data + '\n'; 588 | }); 589 | ls6.stderr.on('data', function (data) { 590 | error += data; 591 | }); 592 | ls6.on('exit', function (code) { 593 | if(code > 0) { 594 | reject(error); 595 | return; 596 | } 597 | if(deleteBranch) { 598 | let ls6 = spawn(gitExecutable, ['branch', '-d', currentBranch], gitOptions); 599 | ls6.stdout.on('data', function (data) { 600 | log += data + '\n'; 601 | }); 602 | ls6.stderr.on('data', function (data) { 603 | error += data; 604 | }); 605 | ls6.on('exit', function (code) { 606 | if(code > 0) { 607 | reject(error); 608 | return; 609 | } 610 | var message = log; 611 | if(code === 0 && error.length > 0) 612 | message += '\n\n' + error; 613 | 614 | resolve(message); 615 | }); 616 | } 617 | else { 618 | var message = log; 619 | if(code === 0 && error.length > 0) 620 | message += '\n\n' + error; 621 | 622 | resolve(message); 623 | } 624 | }); 625 | }); 626 | }); 627 | }); 628 | }); 629 | }); 630 | }); 631 | }); 632 | } 633 | 634 | --------------------------------------------------------------------------------