-
Notifications
You must be signed in to change notification settings - Fork 14
/
Feature.js
175 lines (154 loc) · 5.49 KB
/
Feature.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
const NodeGit = require('nodegit');
const Config = require('./Config');
const constants = require('./constants');
const utils = require('./utils');
/**
* All of this class' functions are attached to `NodeGit.Flow` or a `Flow` instance object
* @class
*/
class Feature {
constructor(repo, config) {
this.repo = repo;
this.config = config;
}
/**
* Starts a git flow "feature"
* @async
* @param {Object} repo The repository to start a feature in
* @param {String} featureName The name of the feature to start
* @param {Object} options Options for start feature
* @return {Branch} The nodegit branch for the feature
*/
static startFeature(repo, featureName, options = {}) {
const {sha} = options;
if (!repo) {
return Promise.reject(new Error(constants.ErrorMessage.REPO_REQUIRED));
}
if (!featureName) {
return Promise.reject(new Error('Feature name is required'));
}
let featureBranchName;
let featureBranch;
return Config.getConfig(repo)
.then((config) => {
const featurePrefix = config['gitflow.prefix.feature'];
const developBranchName = config['gitflow.branch.develop'];
featureBranchName = featurePrefix + featureName;
if (sha) {
return NodeGit.Commit.lookup(repo, sha);
}
return NodeGit.Branch.lookup(
repo,
developBranchName,
NodeGit.Branch.BRANCH.LOCAL
)
.then((developBranch) => NodeGit.Commit.lookup(repo, developBranch.target()));
})
.then((fromCommit) => repo.createBranch(featureBranchName, fromCommit))
.then((_featureBranch) => {
featureBranch = _featureBranch;
return repo.checkoutBranch(featureBranch);
})
.then(() => featureBranch);
}
/**
* Finishes a git flow "feature"
* @async
* @param {Object} repo The repository to finish a feature in
* @param {String} featureName The name of the feature to finish
* @param {Object} options Options for finish feature
* @return {Commit} The commit created by finishing the feature
*/
static finishFeature(repo, featureName, options = {}) {
const {
keepBranch,
isRebase,
preRebaseCallback = () => {},
beforeMergeCallback = () => {},
processMergeMessageCallback,
postMergeCallback = () => {},
beforeRebaseFinishCallback = () => {}
} = options;
if (!repo) {
return Promise.reject(new Error('Repo is required'));
}
if (!featureName) {
return Promise.reject(new Error('Feature name is required'));
}
let developBranch;
let featureBranch;
let developCommit;
let featureCommit;
let cancelDevelopMerge;
let mergeCommit;
let developBranchName;
let featureBranchName;
return Config.getConfig(repo)
.then((config) => {
developBranchName = config['gitflow.branch.develop'];
featureBranchName = config['gitflow.prefix.feature'] + featureName;
return Promise.all(
[developBranchName, featureBranchName]
.map((branchName) => NodeGit.Branch.lookup(repo, branchName, NodeGit.Branch.BRANCH.LOCAL))
);
})
.then((branches) => {
developBranch = branches[0];
featureBranch = branches[1];
return Promise.all(branches.map((branch) => repo.getCommit(branch.target())));
})
.then((commits) => {
developCommit = commits[0];
featureCommit = commits[1];
// If the develop branch and feautre branch point to the same thing do not merge them
// or if the `isRebase` parameter is true do not merge
const isSameCommit = developCommit.id().toString() === featureCommit.id().toString();
cancelDevelopMerge = isSameCommit || isRebase;
if (!cancelDevelopMerge) {
return Promise.resolve(beforeMergeCallback(developBranchName, featureBranchName))
.then(() => utils.Repo.merge(developBranch, featureBranch, repo, processMergeMessageCallback))
.then(utils.InjectIntermediateCallback(postMergeCallback));
} else if (isRebase && !isSameCommit) {
return Promise.resolve(preRebaseCallback(developBranchName, featureBranchName))
.then(() => utils.Repo.rebase(developBranch, featureBranch, repo, beforeRebaseFinishCallback));
}
return Promise.resolve();
})
.then((_mergeCommit) => {
mergeCommit = _mergeCommit;
if (cancelDevelopMerge) {
return repo.checkoutBranch(developBranch);
}
return Promise.resolve();
})
.then(() => {
if (keepBranch) {
return Promise.resolve();
}
return NodeGit.Branch.lookup(repo, featureBranchName, NodeGit.Branch.BRANCH.LOCAL)
.then((branch) => branch.delete());
})
.then(() => mergeCommit);
}
/**
* Starts a git flow "feature"
* @async
* @param {String} featureName The name of the feature to start
* @param {Object} options Options for start feature
* @return {Branch} The nodegit branch for the feature
*/
startFeature() {
return Feature.startFeature(this.repo, ...arguments);
}
/**
* Finishes a git flow "feature"
* @async
* @param {String} featureName The name of the feature to finish
* @param {Object} options Options for finish feature
* @return {Commit} The commit created by finishing the feature
*/
finishFeature() {
return Feature.finishFeature(this.repo, ...arguments);
}
}
module.exports = Feature;