Skip to content
This repository has been archived by the owner on Nov 8, 2017. It is now read-only.

More multiple repository work. #165

Merged
merged 11 commits into from
Jul 16, 2015
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
2 changes: 1 addition & 1 deletion lib/conflicted-editor.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
37 changes: 12 additions & 25 deletions lib/git-bridge.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -102,19 +93,15 @@ 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: (handler) ->
@withConflicts: (repo, handler) ->
return unless @_checkHealth(handler)

conflicts = []
Expand All @@ -140,7 +127,7 @@ class GitBridge
proc = @process({
command: GitCmd,
args: ['status', '--porcelain'],
options: { cwd: @_repoWorkDir() },
options: { cwd: repo.getWorkingDirectory() },
stdout: stdoutHandler,
stderr: stderrHandler,
exit: exitHandler
Expand All @@ -149,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

Expand All @@ -170,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
Expand All @@ -179,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) ->
Expand All @@ -198,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) ->
Expand Down
15 changes: 6 additions & 9 deletions lib/merge-state.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -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
16 changes: 11 additions & 5 deletions lib/view/merge-conflicts-view.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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: ->
Expand Down Expand Up @@ -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()
Expand All @@ -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
Expand Down
10 changes: 5 additions & 5 deletions lib/view/resolver-view.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -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', =>
Expand All @@ -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()
Expand All @@ -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()
Expand All @@ -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()
Expand Down
6 changes: 6 additions & 0 deletions spec/conflicted-editor-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down
24 changes: 16 additions & 8 deletions spec/git-bridge-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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', ->

Expand All @@ -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
Expand All @@ -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] = []
Expand All @@ -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', ->

Expand Down
11 changes: 10 additions & 1 deletion spec/view/merge-conflicts-view-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 9 additions & 3 deletions spec/view/resolver-view-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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')
Expand All @@ -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() })