Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fix: gitWorkflow handles active merge mode
  • Loading branch information
iiroj committed Nov 14, 2019
1 parent effdb4c commit 2f1e886
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 26 deletions.
59 changes: 58 additions & 1 deletion src/gitWorkflow.js
@@ -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']
Expand All @@ -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)
})
}

/**
Expand All @@ -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])
Expand Down Expand Up @@ -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!')
}
}
}

Expand Down
53 changes: 28 additions & 25 deletions test/runAll.unmocked.spec.js
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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 () => {
Expand Down

0 comments on commit 2f1e886

Please sign in to comment.