From f1badc28ce013a317cd964cfcebcf807a562c3ae Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Thu, 16 Jul 2015 11:59:27 -0400 Subject: [PATCH 01/11] Accept an explicit Repository object --- lib/git-bridge.coffee | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lib/git-bridge.coffee b/lib/git-bridge.coffee index cae0bcc..daa6353 100644 --- a/lib/git-bridge.coffee +++ b/lib/git-bridge.coffee @@ -82,15 +82,6 @@ class GitBridge repo = atom.project.getRepositories()[0] return repo - # Public: Return a filepath relative to the git repository that contains it. - # - # * `filepath` {String} Absolute path to the file. - # - # Returns the relative path as a {String}, or null if the path isn't within a known repository. - # - @repoRelativePath: (filepath) -> - @getActiveRepo(filepath)?.relativize filepath - @_repoWorkDir: (filepath) -> @getActiveRepo(filepath).getWorkingDirectory() @_repoGitDir: (filepath) -> @getActiveRepo(filepath).getPath() @@ -114,7 +105,7 @@ class GitBridge return true - @withConflicts: (handler) -> + @withConflicts: (repo, handler) -> return unless @_checkHealth(handler) conflicts = [] @@ -140,7 +131,7 @@ class GitBridge proc = @process({ command: GitCmd, args: ['status', '--porcelain'], - options: { cwd: @_repoWorkDir() }, + options: { cwd: repo.getWorkingDirectory() }, stdout: stdoutHandler, stderr: stderrHandler, exit: exitHandler From 575b3121c0fc4aad886c1ba06d6a6bf1a94a7127 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Thu, 16 Jul 2015 12:59:30 -0400 Subject: [PATCH 02/11] Require an explicit repo for GitBridge calls. --- lib/git-bridge.coffee | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/lib/git-bridge.coffee b/lib/git-bridge.coffee index daa6353..e243573 100644 --- a/lib/git-bridge.coffee +++ b/lib/git-bridge.coffee @@ -93,16 +93,12 @@ class GitBridge [__, indexCode, workCode, p] = m handler(indexCode, workCode, p) - @_checkHealth: (callback, filepath) -> + @_checkHealth: (callback) -> unless GitCmd console.trace("GitBridge method called before locateGitAnd") callback(new Error("GitBridge.locateGitAnd has not been called yet")) return false - unless @getActiveRepo(filepath) - callback(new Error("No git repository detected")) - return false - return true @withConflicts: (repo, handler) -> @@ -140,8 +136,8 @@ class GitBridge proc.process.on 'error', (err) -> handler(new GitNotFoundError(errMessage.join("\n")), null) - @isStaged: (filepath, handler) -> - return unless @_checkHealth(handler, filepath) + @isStaged: (repo, filepath, handler) -> + return unless @_checkHealth(handler) staged = true @@ -161,7 +157,7 @@ class GitBridge proc = @process({ command: GitCmd, args: ['status', '--porcelain', filepath], - options: { cwd: @_repoWorkDir() }, + options: { cwd: repo.getWorkingDirectory() }, stdout: stdoutHandler, stderr: stderrHandler, exit: exitHandler @@ -170,13 +166,13 @@ class GitBridge proc.process.on 'error', (err) -> handler(new GitNotFoundError, null) - @checkoutSide: (sideName, filepath, callback) -> - return unless @_checkHealth(callback, filepath) + @checkoutSide: (repo, sideName, filepath, callback) -> + return unless @_checkHealth(callback) proc = @process({ command: GitCmd, args: ['checkout', "--#{sideName}", filepath], - options: { cwd: @_repoWorkDir() }, + options: { cwd: repo.getWorkingDirectory() }, stdout: (line) -> console.log line stderr: (line) -> console.log line exit: (code) -> @@ -189,13 +185,13 @@ class GitBridge proc.process.on 'error', (err) -> callback(new GitNotFoundError) - @add: (filepath, callback) -> - return unless @_checkHealth(callback, filepath) + @add: (repo, filepath, callback) -> + return unless @_checkHealth(callback) @process({ command: GitCmd, args: ['add', filepath], - options: { cwd: @_repoWorkDir() }, + options: { cwd: repo.getWorkingDirectory() }, stdout: (line) -> console.log line stderr: (line) -> console.log line exit: (code) -> From 176b1031efd67266ca2eb9c917e08fe29a86ed12 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Thu, 16 Jul 2015 13:00:03 -0400 Subject: [PATCH 03/11] Remember a GitRepository within MergeState. --- lib/merge-state.coffee | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/merge-state.coffee b/lib/merge-state.coffee index 8267d01..c4d255f 100644 --- a/lib/merge-state.coffee +++ b/lib/merge-state.coffee @@ -2,26 +2,23 @@ class MergeState - constructor: (@conflicts, @isRebase) -> + constructor: (@conflicts, @repo, @isRebase) -> conflictPaths: -> c.path for c in @conflicts reread: (callback) -> - GitBridge.withConflicts (err, @conflicts) => - if err? - callback(err, null) - else - callback(null, this) + GitBridge.withConflicts @repo, (err, @conflicts) => + callback(err, this) isEmpty: -> @conflicts.length is 0 - @read: (callback) -> + @read: (repo, callback) -> isr = GitBridge.isRebasing() - GitBridge.withConflicts (err, cs) -> + GitBridge.withConflicts repo, (err, cs) -> if err? callback(err, null) else - callback(null, new MergeState(cs, isr)) + callback(null, new MergeState(cs, repo, isr)) module.exports = MergeState: MergeState From 85026be47d5393d191fb0660cc94fb59a0b91275 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Thu, 16 Jul 2015 13:00:26 -0400 Subject: [PATCH 04/11] Use the internal repository in MergeState calls --- lib/view/merge-conflicts-view.coffee | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/view/merge-conflicts-view.coffee b/lib/view/merge-conflicts-view.coffee index 9c2164a..7dd7d8f 100644 --- a/lib/view/merge-conflicts-view.coffee +++ b/lib/view/merge-conflicts-view.coffee @@ -38,7 +38,7 @@ class MergeConflictsView extends View @subs = new CompositeDisposable @subs.add @pkg.onDidResolveConflict (event) => - p = GitBridge.repoRelativePath event.file + p = @state.repo.relativize event.file found = false for listElement in @pathList.children() li = $(listElement) @@ -153,10 +153,16 @@ class MergeConflictsView extends View @pkg.didStageFile file: filePath @detect: (pkg) -> - return unless atom.project.getRepositories().length > 0 return if @instance? - MergeState.read (err, state) => + repo = GitBridge.getActiveRepo() + unless repo? + atom.notifications.addWarning "No git repository found", + detail: "Tip: if you have multiple projects open, open an editor in the one" + "containing conflicts." + return + + MergeState.read repo, (err, state) => return if handleErr(err) if not state.isEmpty() @@ -176,7 +182,7 @@ class MergeConflictsView extends View return if state.isEmpty() fullPath = editor.getPath() - repoPath = GitBridge.repoRelativePath fullPath + repoPath = state.repo.relativize fullPath return unless repoPath? return unless _.contains state.conflictPaths(), repoPath From 534bfcbd93428131afeef83b0ce322965025c8c3 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Thu, 16 Jul 2015 13:01:58 -0400 Subject: [PATCH 05/11] CoffeeScript strings do not work that way --- lib/view/merge-conflicts-view.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/view/merge-conflicts-view.coffee b/lib/view/merge-conflicts-view.coffee index 7dd7d8f..94c9d86 100644 --- a/lib/view/merge-conflicts-view.coffee +++ b/lib/view/merge-conflicts-view.coffee @@ -158,8 +158,8 @@ class MergeConflictsView extends View repo = GitBridge.getActiveRepo() unless repo? atom.notifications.addWarning "No git repository found", - detail: "Tip: if you have multiple projects open, open an editor in the one" - "containing conflicts." + detail: "Tip: if you have multiple projects open, open an editor in the one + containing conflicts." return MergeState.read repo, (err, state) => From 0020a3b5ee825f33be5089cb9118aa21aaacf7b7 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Thu, 16 Jul 2015 13:08:20 -0400 Subject: [PATCH 06/11] Pass the MergeState to ResolverView --- lib/conflicted-editor.coffee | 2 +- lib/view/resolver-view.coffee | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/conflicted-editor.coffee b/lib/conflicted-editor.coffee index 356baa4..5396f1e 100644 --- a/lib/conflicted-editor.coffee +++ b/lib/conflicted-editor.coffee @@ -102,7 +102,7 @@ class ConflictedEditor # Private: Event handler invoked when all conflicts in this file have been resolved. # conflictsResolved: -> - atom.workspace.addTopPanel item: new ResolverView(@editor, @pkg) + atom.workspace.addTopPanel item: new ResolverView(@editor, @state, @pkg) detectDirty: -> # Only detect dirty regions within CoveringViews that have a cursor within them. diff --git a/lib/view/resolver-view.coffee b/lib/view/resolver-view.coffee index 86d314c..b74e307 100644 --- a/lib/view/resolver-view.coffee +++ b/lib/view/resolver-view.coffee @@ -8,7 +8,7 @@ class ResolverView extends View - @content: (editor, pkg) -> + @content: (editor, state, pkg) -> @div class: 'overlay from-top resolver', => @div class: 'block text-highlight', "We're done here" @div class: 'block', => @@ -22,7 +22,7 @@ class ResolverView extends View @div class: 'pull-right', => @button class: 'btn btn-primary', click: 'resolve', 'Stage' - initialize: (@editor, @pkg) -> + initialize: (@editor, @state, @pkg) -> @subs = new CompositeDisposable() @refresh() @@ -35,10 +35,10 @@ class ResolverView extends View getModel: -> null relativePath: -> - GitBridge.getActiveRepo().relativize @editor.getURI() + @state.repo.relativize @editor.getURI() refresh: -> - GitBridge.isStaged @relativePath(), (err, staged) => + GitBridge.isStaged @state.repo, @relativePath(), (err, staged) => return if handleErr(err) modified = @editor.isModified() @@ -58,7 +58,7 @@ class ResolverView extends View resolve: -> @editor.save() - GitBridge.add @relativePath(), (err) => + GitBridge.add @state.repo, @relativePath(), (err) => return if handleErr(err) @refresh() From 71f60189b3ded35ab20080c8c7808b21bf289e76 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Thu, 16 Jul 2015 13:19:23 -0400 Subject: [PATCH 07/11] Bring GitBridge specs up to date. --- spec/git-bridge-spec.coffee | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/spec/git-bridge-spec.coffee b/spec/git-bridge-spec.coffee index dae643e..af90a1f 100644 --- a/spec/git-bridge-spec.coffee +++ b/spec/git-bridge-spec.coffee @@ -4,7 +4,15 @@ path = require 'path' describe 'GitBridge', -> - repoBase = -> atom.project.getRepositories()[0].getWorkingDirectory() + gitWorkDir = "/fake/gitroot/" + + repo = + getWorkingDirectory: -> gitWorkDir + relativize: (fullpath) -> + if fullpath.startsWith gitWorkDir + fullpath[gitWorkDir.length..] + else + fullpath beforeEach -> done = false @@ -27,7 +35,7 @@ describe 'GitBridge', -> { process: { on: (callback) -> } } conflicts = [] - GitBridge.withConflicts (err, cs) -> + GitBridge.withConflicts repo, (err, cs) -> throw err if err conflicts = cs @@ -37,7 +45,7 @@ describe 'GitBridge', -> ]) expect(c).toBe('/usr/bin/git') expect(a).toEqual(['status', '--porcelain']) - expect(o).toEqual({ cwd: repoBase() }) + expect(o).toEqual({ cwd: gitWorkDir }) describe 'isStaged', -> @@ -48,7 +56,7 @@ describe 'GitBridge', -> { process: { on: (callback) -> } } staged = null - GitBridge.isStaged checkPath, (err, b) -> + GitBridge.isStaged repo, checkPath, (err, b) -> throw err if err staged = b staged @@ -73,14 +81,14 @@ describe 'GitBridge', -> { process: { on: (callback) -> } } called = false - GitBridge.checkoutSide 'ours', 'lib/file1.txt', (err) -> + GitBridge.checkoutSide repo, 'ours', 'lib/file1.txt', (err) -> throw err if err called = true expect(called).toBe(true) expect(c).toBe('/usr/bin/git') expect(a).toEqual(['checkout', '--ours', 'lib/file1.txt']) - expect(o).toEqual({ cwd: repoBase() }) + expect(o).toEqual({ cwd: gitWorkDir }) it 'stages changes to a file', -> [c, a, o] = [] @@ -89,14 +97,14 @@ describe 'GitBridge', -> exit(0) called = false - GitBridge.add 'lib/file1.txt', (err) -> + GitBridge.add repo, 'lib/file1.txt', (err) -> throw err if err called = true expect(called).toBe(true) expect(c).toBe('/usr/bin/git') expect(a).toEqual(['add', 'lib/file1.txt']) - expect(o).toEqual({ cwd: repoBase() }) + expect(o).toEqual({ cwd: gitWorkDir }) describe 'rebase detection', -> From e4c663b37aff25731f788604f441d9e6fc72a57b Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Thu, 16 Jul 2015 13:26:34 -0400 Subject: [PATCH 08/11] ResolverView specs. --- spec/view/resolver-view-spec.coffee | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/spec/view/resolver-view-spec.coffee b/spec/view/resolver-view-spec.coffee index 1c63c4f..0547fc1 100644 --- a/spec/view/resolver-view-spec.coffee +++ b/spec/view/resolver-view-spec.coffee @@ -6,15 +6,19 @@ util = require '../util' describe 'ResolverView', -> [view, fakeEditor, pkg] = [] + state = + repo: + getWorkingDirectory: -> "/fake/gitroot/" + relativize: (filepath) -> filepath["/fake/gitroot/".length..] + beforeEach -> pkg = util.pkgEmitter() fakeEditor = { isModified: -> true - getURI: -> 'lib/file1.txt' + getURI: -> '/fake/gitroot/lib/file1.txt' save: -> onDidSave: -> } - view = new ResolverView(fakeEditor, pkg) atom.config.set('merge-conflicts.gitPath', 'git') done = false @@ -29,6 +33,8 @@ describe 'ResolverView', -> exit(0) { process: { on: (err) -> } } + view = new ResolverView(fakeEditor, state, pkg) + it 'begins needing both saving and staging', -> view.refresh() expect(view.actionText.text()).toBe('Save and stage') @@ -55,4 +61,4 @@ describe 'ResolverView', -> expect(fakeEditor.save).toHaveBeenCalled() expect(c).toBe('git') expect(a).toEqual(['add', 'lib/file1.txt']) - expect(o).toEqual({ cwd: atom.project.getRepositories()[0].getWorkingDirectory() }) + expect(o).toEqual({ cwd: state.repo.getWorkingDirectory() }) From 1fbbf11d3ba5204ab6bec6a2672121a69d63f6e6 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Thu, 16 Jul 2015 13:32:24 -0400 Subject: [PATCH 09/11] Stub the repo in ConflictedEditor specs. --- spec/conflicted-editor-spec.coffee | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/conflicted-editor-spec.coffee b/spec/conflicted-editor-spec.coffee index 941b349..c027abb 100644 --- a/spec/conflicted-editor-spec.coffee +++ b/spec/conflicted-editor-spec.coffee @@ -52,6 +52,9 @@ describe 'ConflictedEditor', -> editor = editorView.getModel() state = isRebase: false + repo: + getWorkingDirectory: -> "" + relativize: (filepath) -> filepath m = new ConflictedEditor(state, pkg, editor) m.mark() @@ -209,6 +212,9 @@ describe 'ConflictedEditor', -> editor = editorView.getModel() state = isRebase: true + repo: + getWorkingDirectory: -> "" + relativize: (filepath) -> filepath m = new ConflictedEditor(state, pkg, editor) m.mark() From af35c96e42d9d5818c8b0c5ee3a323c5ff883785 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Thu, 16 Jul 2015 13:55:13 -0400 Subject: [PATCH 10/11] Use a real repo in those specs, why not. --- lib/view/merge-conflicts-view.coffee | 2 +- spec/view/merge-conflicts-view-spec.coffee | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/view/merge-conflicts-view.coffee b/lib/view/merge-conflicts-view.coffee index 94c9d86..e6932a8 100644 --- a/lib/view/merge-conflicts-view.coffee +++ b/lib/view/merge-conflicts-view.coffee @@ -62,7 +62,7 @@ class MergeConflictsView extends View navigate: (event, element) -> repoPath = element.find(".path").text() - fullPath = path.join GitBridge.getActiveRepo().getWorkingDirectory(), repoPath + fullPath = path.join @state.repo.getWorkingDirectory(), repoPath atom.workspace.open(fullPath) minimize: -> diff --git a/spec/view/merge-conflicts-view-spec.coffee b/spec/view/merge-conflicts-view-spec.coffee index 654fdc4..c5d059d 100644 --- a/spec/view/merge-conflicts-view-spec.coffee +++ b/spec/view/merge-conflicts-view-spec.coffee @@ -20,11 +20,20 @@ describe 'MergeConflictsView', -> beforeEach -> pkg = util.pkgEmitter() + GitBridge.process = ({exit}) -> + exit(0) + { process: { on: (err) -> }, onWillThrowError: -> } + + done = false + GitBridge.locateGitAnd (err) -> done = true + waitsFor -> done + conflicts = _.map ['file1.txt', 'file2.txt'], (fname) -> { path: repoPath(fname), message: 'both modified' } util.openPath 'triple-2way-diff.txt', (editorView) -> - state = new MergeState conflicts, false + repo = atom.project.getRepositories()[0] + state = new MergeState conflicts, repo, false conflicts = Conflict.all state, editorView.getModel() view = new MergeConflictsView(state, pkg) From c80dbeecd6b7ac02e652c64cd495c1f5588d8640 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Thu, 16 Jul 2015 13:59:48 -0400 Subject: [PATCH 11/11] CHANGELOG entry. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e54f91b..c8dd099 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## 1.3.3 +- With multiple projects, remember the git repository that you initially detected conflicts within. [#165](https://github.com/smashwilson/merge-conflicts/pull/165) - Handle projects with no git repository. [#164](https://github.com/smashwilson/merge-conflicts/pull/164) - Improve the "Git not found" error dialog. [#163](https://github.com/smashwilson/merge-conflicts/pull/163) - Use alt-m instead of ctrl-m in key bindings. [#162](https://github.com/smashwilson/merge-conflicts/pull/162)