feat: Allow to recover from ENOTINHISTORY with a tag and handle detached head repo

- Tag sha will now be used also if there is a gitHead in last release and it's not in the history
- Use `git merge-base` to determine if a commit is in history, allowing to use CI creating detached head repo
- Mention recovery solution by creating a version tag in `ENOTINHISTORY` and `ENOGITHEAD` error messages
- Do not mention branches containing missing commit in `ENOTINHISTORY` and `ENOGITHEAD` error messages as it's not available by default on most CI
This commit is contained in:
Pierre-Denis Vanduynslager
2017-10-11 20:54:10 -07:00
committed by Gregor Martynus
parent 8e9d9f77f3
commit 580ad9c3d2
5 changed files with 365 additions and 110 deletions
+41 -6
View File
@@ -35,7 +35,7 @@ export async function gitRepo() {
*
* @param {Array<string>} messages commit messages.
*
* @returns {Array<Commit>} commits the created commits, in reverse order (to match `git log` order).
* @returns {Array<Commit>} The created commits, in reverse order (to match `git log` order).
*/
export async function gitCommits(messages) {
return (await pMapSeries(messages, async msg => {
@@ -45,6 +45,19 @@ export async function gitCommits(messages) {
})).reverse();
}
/**
* Amend a commit (rewriting the sha) on the current git repository.
*
* @param {string} messages commit message.
*
* @returns {Array<Commit>} the created commits.
*/
export async function gitAmmendCommit(msg) {
const {stdout} = await execa('git', ['commit', '--amend', '-m', msg, '--allow-empty']);
const [, branch, hash, message] = /^\[(\w+)\(?.*?\)?(\w+)\] (.+)(.|\s)+$/.exec(stdout);
return {branch, hash, message};
}
/**
* Checkout a branch on the current git repository.
*
@@ -52,7 +65,7 @@ export async function gitCommits(messages) {
* @param {boolean} create `true` to create the branche ans switch, `false` to only switch.
*/
export async function gitCheckout(branch, create = true) {
await execa('git', ['checkout', create ? '-b' : null, branch]);
await execa('git', create ? ['checkout', '-b', branch] : ['checkout', branch]);
}
/**
@@ -66,9 +79,13 @@ export async function gitHead() {
* Create a tag on the head commit in the current git repository.
*
* @param {string} tagName The tag name to create.
* @param {string} [sha] The commit on which to create the tag. If undefined the tag is created on the last commit.
*
* @return {string} The commit sha of the created tag.
*/
export async function gitTagVersion(tagName) {
await execa('git', ['tag', tagName]);
export async function gitTagVersion(tagName, sha) {
await execa('git', sha ? ['tag', '-f', tagName, sha] : ['tag', tagName]);
return (await execa('git', ['rev-list', '-1', '--tags', tagName])).stdout;
}
/**
@@ -93,11 +110,29 @@ export async function gitLog() {
* @param {number} [depth=1] The number of commit to clone.
* @return {string} The path of the cloned repository.
*/
export async function gitShallowClone(origin, depth = 1) {
export async function gitShallowClone(origin, branch = 'master', depth = 1) {
const dir = tempy.directory();
process.chdir(dir);
await execa('git', ['clone', '--no-hardlinks', '--no-tags', '--depth', depth, `file://${origin}`, dir]);
await execa('git', ['clone', '--no-hardlinks', '--no-tags', '-b', branch, '--depth', depth, `file://${origin}`, dir]);
return dir;
}
/**
* Create a git repo with a detached head from another git repository and change the current working directory to the new repository root.
*
* @param {string} origin The path of the repository to clone.
* @param {number} head A commit sha of the origin repo that will become the detached head of the new one.
* @return {string} The path of the new repository.
*/
export async function gitDetachedHead(origin, head) {
const dir = tempy.directory();
process.chdir(dir);
await execa('git', ['init']);
await execa('git', ['remote', 'add', 'origin', origin]);
await execa('git', ['fetch']);
await execa('git', ['checkout', head]);
return dir;
}