Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: gitWorkflow handles active merge mode
Browse files Browse the repository at this point in the history
iiroj authored and Andrey Okonetchnikov committed Oct 31, 2019

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 89e6de0 commit 959d9d9
Showing 2 changed files with 86 additions and 26 deletions.
59 changes: 58 additions & 1 deletion src/gitWorkflow.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
'use strict'

const debug = require('debug')('lint-staged:git')
const fs = require('fs')
const path = require('path')

const execGit = require('./execGit')

const MERGE_HEAD = 'MERGE_HEAD'
const MERGE_MODE = 'MERGE_MODE'
const MERGE_MSG = 'MERGE_MSG'

const STASH = 'lint-staged automatic backup'

const gitApplyArgs = ['apply', '-v', '--whitespace=nowarn', '--recount', '--unidiff-zero']
@@ -12,6 +18,33 @@ class GitWorkflow {
constructor(cwd) {
this.execGit = (args, options = {}) => execGit(args, { ...options, cwd })
this.unstagedDiff = null
this.cwd = cwd
}

/**
* Read file from .git directory, returning a buffer or null
* @param {String} filename Relative path to file
* @returns {Promise<Buffer|Null>}
*/
readGitConfigFile(filename) {
const resolvedPath = path.resolve(this.cwd, '.git', filename)
return new Promise(resolve => {
fs.readFile(resolvedPath, (error, file) => {
resolve(error && error.code === 'ENOENT' ? null : file)
})
})
}

/**
* Write buffer to relative .git directory
* @param {String} filename Relative path to file
* @param {Buffer} buffer
*/
writeGitConfigFile(filename, buffer) {
const resolvedPath = path.resolve(this.cwd, '.git', filename)
return new Promise(resolve => {
fs.writeFile(resolvedPath, buffer, resolve)
})
}

/**
@@ -35,6 +68,21 @@ class GitWorkflow {
*/
async stashBackup() {
debug('Backing up original state...')

// Git stash loses metadata about a possible merge mode
// Manually check and backup if necessary
const mergeHead = await this.readGitConfigFile(MERGE_HEAD)
if (mergeHead) {
debug('Detected current merge mode!')
debug('Backing up merge state...')
this.mergeHead = mergeHead
await Promise.all([
this.readGitConfigFile(MERGE_MODE).then(mergeMode => (this.mergeMode = mergeMode)),
this.readGitConfigFile(MERGE_MSG).then(mergeMsg => (this.mergeMsg = mergeMsg))
])
debug('Done backing up merge state!')
}

// Get stash of entire original state, including unstaged changes
// Keep index so that tasks only work on those files
await this.execGit(['stash', 'save', '--quiet', '--include-untracked', '--keep-index', STASH])
@@ -100,8 +148,17 @@ class GitWorkflow {
debug('Dropping backup stash...')
const original = await this.getBackupStash()
await this.execGit(['stash', 'drop', '--quiet', original])
this.unstagedDiff = null
debug('Done dropping backup stash!')

if (this.mergeHead) {
debug('Detected backup merge state!')
debug('Restoring merge state...')
const writePromises = [this.writeGitConfigFile(MERGE_HEAD, this.mergeHead)]
if (this.mergeMode) writePromises.push(this.writeGitConfigFile(MERGE_MODE, this.mergeMode))
if (this.mergeMsg) writePromises.push(this.writeGitConfigFile(MERGE_MSG, this.mergeMsg))
await Promise.all(writePromises)
debug('Done restoring merge state!')
}
}
}

53 changes: 28 additions & 25 deletions test/runAll.unmocked.spec.js
Original file line number Diff line number Diff line change
@@ -300,17 +300,17 @@ describe('runAll', () => {
// But local modifications are gone
expect(await execGit(['diff'])).not.toEqual(diff)
expect(await execGit(['diff'])).toMatchInlineSnapshot(`
"diff --git a/test.js b/test.js
index f80f875..1c5643c 100644
--- a/test.js
+++ b/test.js
@@ -1,3 +1,3 @@
module.exports = {
- 'foo': 'bar',
-}
+ foo: \\"bar\\"
+};"
`)
"diff --git a/test.js b/test.js
index f80f875..1c5643c 100644
--- a/test.js
+++ b/test.js
@@ -1,3 +1,3 @@
module.exports = {
- 'foo': 'bar',
-}
+ foo: \\"bar\\"
+};"
`)

expect(await readFile('test.js')).not.toEqual(testJsFileUgly + appended)
expect(await readFile('test.js')).toEqual(testJsFilePretty)
@@ -366,13 +366,13 @@ describe('runAll', () => {
}

expect(await readFile('test.js')).toMatchInlineSnapshot(`
"<<<<<<< HEAD
module.exports = \\"foo\\";
=======
module.exports = \\"bar\\";
>>>>>>> branch-b
"
`)
"<<<<<<< HEAD
module.exports = \\"foo\\";
=======
module.exports = \\"bar\\";
>>>>>>> branch-b
"
`)

// Fix conflict and commit using lint-staged
await writeFile('test.js', fileInBranchB)
@@ -381,15 +381,18 @@ describe('runAll', () => {

// Do not use `gitCommit` wrapper here
await runAll({ ...fixJsConfig, cwd, quiet: true })
await execGit(['commit', '--no-edit'])

// Lint-staged lost MERGE_HEAD
try {
await execGit(['merge', '--continue'])
} catch ({ stderr }) {
expect(stderr).toMatch('There is no merge in progress (MERGE_HEAD missing)')
}
// Nothing is wrong, so a new commit is created and file is pretty
expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('4')
expect(await execGit(['log', '-1', '--pretty=%B'])).toMatchInlineSnapshot(`
"Merge branch 'branch-b'
// TODO: Fix behaviour by saving/restoring MERGE_HEAD, and then complete test
# Conflicts:
# test.js
"
`)
expect(await readFile('test.js')).toEqual(fileInBranchBFixed)
})

afterEach(async () => {

0 comments on commit 959d9d9

Please sign in to comment.