├── .gitignore ├── .npmignore ├── commit.gif ├── .gitmodules ├── lib ├── views │ ├── remote-branch-list-view.coffee │ ├── status-view.coffee │ ├── output-view.coffee │ ├── pull-branch-list-view.coffee │ ├── select-stage-hunk-file-view.coffee │ ├── delete-branch-view.coffee │ ├── repo-list-view.coffee │ ├── projects-list-view.coffee │ ├── tag-list-view.coffee │ ├── cherry-pick-select-branch-view.coffee │ ├── merge-list-view.coffee │ ├── branch-list-view.coffee │ ├── select-unstage-files-view.coffee │ ├── select-stage-files-view.coffee │ ├── tag-create-view.coffee │ ├── tag-view.coffee │ ├── cherry-pick-select-commits-view.coffee │ ├── remove-list-view.coffee │ ├── status-list-view.coffee │ ├── remote-list-view.coffee │ ├── select-stage-hunks-view.coffee │ ├── git-palette-view.coffee │ ├── log-list-view.coffee │ └── select-list-multiple-view.coffee ├── models │ ├── git-status.coffee │ ├── git-add-all-and-commit.coffee │ ├── git-unstage-files.coffee │ ├── git-merge.coffee │ ├── git-add.coffee │ ├── git-stage-hunk.coffee │ ├── git-add-all-commit-and-push.coffee │ ├── git-add-and-commit.coffee │ ├── git-stage-files.coffee │ ├── git-fetch.coffee │ ├── git-push.coffee │ ├── git-fetch-prune.coffee │ ├── git-delete-local-branch.coffee │ ├── git-delete-remote-branch.coffee │ ├── git-checkout-all-files.coffee │ ├── git-diff-all.coffee │ ├── git-pull.coffee │ ├── git-tags.coffee │ ├── git-stash-pop.coffee │ ├── git-stash-drop.coffee │ ├── git-commit-amend.coffee │ ├── git-stash-save.coffee │ ├── git-stash-apply.coffee │ ├── git-cherry-pick.coffee │ ├── git-checkout-current-file.coffee │ ├── git-init.coffee │ ├── git-log.coffee │ ├── git-remove.coffee │ ├── git-run.coffee │ ├── git-branch.coffee │ ├── git-diff.coffee │ ├── git-show.coffee │ ├── fuzzy.coffee │ └── git-commit.coffee ├── notifier.coffee ├── git-plus-commands.coffee ├── git.coffee └── git-plus.coffee ├── spec └── git-spec.coffee ├── LICENSE.md ├── keymaps └── git-plus.cson ├── menus └── git-plus.cson ├── styles └── git-plus.less ├── package.json ├── grammars └── diff.cson ├── README.md └── Changelog.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | doc 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | doc 5 | -------------------------------------------------------------------------------- /commit.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darobin/git-plus/master/commit.gif -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test-submodule"] 2 | path = spec/foo 3 | url = git@github.com:akonwi/foo 4 | -------------------------------------------------------------------------------- /lib/views/remote-branch-list-view.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | BranchListView = require '../views/branch-list-view' 3 | 4 | module.exports = 5 | class RemoteBranchListView extends BranchListView 6 | args: ['checkout', '-t'] 7 | -------------------------------------------------------------------------------- /lib/models/git-status.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | StatusListView = require '../views/status-list-view' 3 | 4 | gitStatus = (repo) -> 5 | git.status repo, (data) -> new StatusListView(repo, data) 6 | 7 | module.exports = gitStatus 8 | -------------------------------------------------------------------------------- /lib/models/git-add-all-and-commit.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | GitCommit = require './git-commit' 3 | 4 | gitAddAllAndCommit = (repo) -> 5 | git.add repo, 6 | exit: -> new GitCommit(repo) 7 | 8 | module.exports = gitAddAllAndCommit 9 | -------------------------------------------------------------------------------- /lib/models/git-unstage-files.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | SelectUnstageFiles = require '../views/select-unstage-files-view' 3 | 4 | gitUnstageFiles = (repo) -> 5 | git.stagedFiles repo, (data) -> new SelectUnstageFiles(repo, data) 6 | 7 | module.exports = gitUnstageFiles 8 | -------------------------------------------------------------------------------- /lib/models/git-merge.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | MergeListView = require '../views/merge-list-view' 3 | 4 | module.exports = (repo) -> 5 | git.cmd 6 | args: ['branch'] 7 | cwd: repo.getWorkingDirectory() 8 | stdout: (data) -> 9 | new MergeListView(repo, data) 10 | -------------------------------------------------------------------------------- /lib/models/git-add.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | 3 | gitAdd = (repo, {addAll}={}) -> 4 | if not addAll 5 | file = repo.relativize(atom.workspace.getActiveTextEditor()?.getPath()) 6 | else 7 | file = null 8 | 9 | git.add(repo, file: file) 10 | 11 | module.exports = gitAdd 12 | -------------------------------------------------------------------------------- /lib/models/git-stage-hunk.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | SelectStageHunkFile = require '../views/select-stage-hunk-file-view' 3 | 4 | gitStageHunk = (repo) -> 5 | git.unstagedFiles(repo, null, 6 | (data) -> new SelectStageHunkFile(repo, data) 7 | ) 8 | 9 | module.exports = gitStageHunk 10 | -------------------------------------------------------------------------------- /lib/models/git-add-all-commit-and-push.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | GitCommit = require './git-commit' 3 | 4 | gitAddAllCommitAndPush = (repo) -> 5 | git.add repo, 6 | file: null, 7 | exit: -> 8 | new GitCommit(repo, andPush: true) 9 | 10 | module.exports = gitAddAllCommitAndPush 11 | -------------------------------------------------------------------------------- /lib/models/git-add-and-commit.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | GitCommit = require './git-commit' 3 | 4 | gitAddAndCommit = (repo) -> 5 | git.add repo, 6 | file: repo.relativize(atom.workspace.getActiveTextEditor()?.getPath()) 7 | exit: -> new GitCommit(repo) 8 | 9 | module.exports = gitAddAndCommit 10 | -------------------------------------------------------------------------------- /lib/models/git-stage-files.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | SelectStageFiles = require '../views/select-stage-files-view' 3 | 4 | gitStageFiles = (repo) -> 5 | git.unstagedFiles(repo, 6 | showUntracked: true, 7 | (data) -> new SelectStageFiles(repo, data) 8 | ) 9 | 10 | module.exports = gitStageFiles 11 | -------------------------------------------------------------------------------- /lib/models/git-fetch.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | ListView = require '../views/remote-list-view' 3 | 4 | gitFetch = (repo) -> 5 | git.cmd 6 | args: ['remote'] 7 | cwd: repo.getWorkingDirectory() 8 | stdout: (data) -> new ListView(repo, data.toString(), mode: 'fetch') 9 | 10 | module.exports = gitFetch 11 | -------------------------------------------------------------------------------- /lib/models/git-push.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | RemoteListView = require '../views/remote-list-view' 3 | 4 | gitPush = (repo) -> 5 | git.cmd 6 | args: ['remote'] 7 | cwd: repo.getWorkingDirectory() 8 | stdout: (data) -> new RemoteListView(repo, data, mode: 'push') 9 | 10 | module.exports = gitPush 11 | -------------------------------------------------------------------------------- /lib/models/git-fetch-prune.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | ListView = require '../views/remote-list-view' 3 | 4 | gitFetch = (repo) -> 5 | git.cmd 6 | args: ['remote'] 7 | cwd: repo.getWorkingDirectory() 8 | stdout: (data) -> new ListView(repo, data.toString(), mode: 'fetch-prune') 9 | 10 | module.exports = gitFetch 11 | -------------------------------------------------------------------------------- /lib/models/git-delete-local-branch.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | ListView = require '../views/delete-branch-view' 3 | 4 | gitDeleteLocalBranch = (repo) -> 5 | git.cmd 6 | args: ['branch'] 7 | cwd: repo.getWorkingDirectory() 8 | stdout: (data) -> new ListView(repo, data.toString()) 9 | 10 | module.exports = gitDeleteLocalBranch 11 | -------------------------------------------------------------------------------- /lib/models/git-delete-remote-branch.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | ListView = require '../views/delete-branch-view' 3 | 4 | gitDeleteRemoteBranch = (repo) -> 5 | git.cmd 6 | args: ['branch', '-r'] 7 | cwd: repo.getWorkingDirectory() 8 | stdout: (data) -> 9 | new ListView(repo, data.toString(), isRemote: true) 10 | 11 | module.exports = gitDeleteRemoteBranch 12 | -------------------------------------------------------------------------------- /lib/models/git-checkout-all-files.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | notifier = require '../notifier' 3 | 4 | gitCheckoutAllFiles = (repo) -> 5 | git.cmd 6 | args: ['checkout', '-f'] 7 | cwd: repo.getWorkingDirectory() 8 | stdout: (data) -> 9 | notifier.addSuccess "File changes checked out successfully!" 10 | git.refresh() 11 | 12 | module.exports = gitCheckoutAllFiles 13 | -------------------------------------------------------------------------------- /lib/models/git-diff-all.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | GitDiff = require './git-diff' 3 | 4 | gitStat = (repo) -> 5 | args = ['diff', '--stat'] 6 | args.push 'HEAD' if atom.config.get 'git-plus.includeStagedDiff' 7 | git.cmd 8 | args: args 9 | cwd: repo.getWorkingDirectory() 10 | stdout: (data) -> GitDiff(repo, diffStat: data, file: '.') 11 | 12 | module.exports = gitStat 13 | -------------------------------------------------------------------------------- /lib/models/git-pull.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | RemoteListView = require '../views/remote-list-view' 3 | 4 | gitPull = (repo, {rebase}={}) -> 5 | extraArgs = ['--rebase'] if rebase 6 | 7 | git.cmd 8 | args: ['remote'] 9 | cwd: repo.getWorkingDirectory() 10 | stdout: (data) -> new RemoteListView(repo, data, mode: 'pull', extraArgs: extraArgs) 11 | 12 | module.exports = gitPull 13 | -------------------------------------------------------------------------------- /lib/models/git-tags.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | TagListView = require '../views/tag-list-view' 3 | 4 | gitTags = (repo) -> 5 | @TagListView = null 6 | git.cmd 7 | args: ['tag', '-ln'] 8 | cwd: repo.getWorkingDirectory() 9 | stdout: (data) -> @TagListView = new TagListView(repo, data), 10 | exit: -> new TagListView(repo) if not @TagListView? 11 | 12 | module.exports = gitTags 13 | -------------------------------------------------------------------------------- /lib/notifier.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | title: 'Git-Plus' 3 | addInfo: (message, {dismissable}={}) -> 4 | atom.notifications.addInfo(@title, detail: message, dismissable: dismissable) 5 | addSuccess: (message, {dismissable}={}) -> 6 | atom.notifications.addSuccess(@title, detail: message, dismissable: dismissable) 7 | addError: (message, {dismissable}={}) -> 8 | atom.notifications.addError(@title, detail: message, dismissable: dismissable) 9 | -------------------------------------------------------------------------------- /lib/models/git-stash-pop.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | notifier = require '../notifier' 3 | 4 | gitStashPop = (repo) -> 5 | git.cmd 6 | args: ['stash', 'pop'] 7 | cwd: repo.getWorkingDirectory() 8 | options: { 9 | env: process.env.NODE_ENV 10 | } 11 | stdout: (data) -> 12 | notifier.addSuccess(data) if data.toString().length > 0 13 | stderr: (data) -> 14 | notifier.addError(data) 15 | 16 | module.exports = gitStashPop 17 | -------------------------------------------------------------------------------- /lib/models/git-stash-drop.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | notifier = require '../notifier' 3 | 4 | gitStashDrop = (repo) -> 5 | git.cmd 6 | args: ['stash', 'drop'] 7 | cwd: repo.getWorkingDirectory() 8 | options: { 9 | env: process.env.NODE_ENV 10 | } 11 | stdout: (data) -> 12 | notifier.addSuccess(data) if data.toString().length > 0 13 | stderr: (data) -> 14 | notifier.addError(data) 15 | 16 | module.exports = gitStashDrop 17 | -------------------------------------------------------------------------------- /lib/models/git-commit-amend.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | GitCommit = require './git-commit' 3 | 4 | gitCommitAmend = (repo) -> 5 | git.cmd 6 | args: ['log', '-1', '--format=%B'] 7 | cwd: repo.getWorkingDirectory() 8 | stdout: (amend) -> 9 | git.cmd 10 | args: ['reset', '--soft', 'HEAD^'] 11 | cwd: repo.getWorkingDirectory() 12 | exit: -> new GitCommit(repo, amend: "#{amend?.trim()}\n") 13 | 14 | module.exports = gitCommitAmend 15 | -------------------------------------------------------------------------------- /lib/models/git-stash-save.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | notifier = require '../notifier' 3 | 4 | gitStashSave = (repo) -> 5 | notification = notifier.addInfo('Saving...', dismissable: true) 6 | git.cmd 7 | args: ['stash', 'save'] 8 | cwd: repo.getWorkingDirectory() 9 | options: { 10 | env: process.env.NODE_ENV 11 | } 12 | stdout: (data) -> 13 | notification.dismiss() 14 | notifier.addSuccess(data) 15 | 16 | module.exports = gitStashSave 17 | -------------------------------------------------------------------------------- /lib/models/git-stash-apply.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | notifier = require '../notifier' 3 | 4 | gitStashApply = (repo) -> 5 | git.cmd 6 | args: ['stash', 'apply'] 7 | cwd: repo.getWorkingDirectory() 8 | options: { 9 | env: process.env.NODE_ENV 10 | } 11 | stdout: (data) -> 12 | notifier.addSuccess(data) if data.toString().length > 0 13 | stderr: (data) -> 14 | notifier.addError(data.toString()) 15 | 16 | module.exports = gitStashApply 17 | -------------------------------------------------------------------------------- /lib/models/git-cherry-pick.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | CherryPickSelectBranch = require '../views/cherry-pick-select-branch-view' 3 | 4 | gitCherryPick = (repo) -> 5 | heads = repo.getReferences().heads 6 | currentHead = repo.getShortHead() 7 | 8 | for head, i in heads 9 | heads[i] = head.replace('refs/heads/', '') 10 | 11 | heads = heads.filter (head) -> head isnt currentHead 12 | new CherryPickSelectBranch(repo, heads, currentHead) 13 | 14 | module.exports = gitCherryPick 15 | -------------------------------------------------------------------------------- /lib/views/status-view.coffee: -------------------------------------------------------------------------------- 1 | {$, View} = require 'atom-space-pen-views' 2 | 3 | module.exports = 4 | class StatusView extends View 5 | @content = (params) -> 6 | @div class: 'git-plus', => 7 | @div class: "#{params.type} message", params.message 8 | 9 | initialize: -> 10 | @panel ?= atom.workspace.addBottomPanel(item: this) 11 | setTimeout => 12 | @destroy() 13 | , atom.config.get('git-plus.messageTimeout') * 1000 14 | 15 | destroy: -> 16 | @panel?.destroy() 17 | -------------------------------------------------------------------------------- /lib/models/git-checkout-current-file.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | notifier = require '../notifier' 3 | 4 | gitCheckoutCurrentFile = (repo)-> 5 | currentFile = repo.relativize(atom.workspace.getActiveTextEditor()?.getPath()) 6 | git.cmd 7 | args: ['checkout', '--', currentFile] 8 | cwd: repo.getWorkingDirectory() 9 | stdout: (data) -> # There is no output from this command 10 | notifier.addSuccess 'File changes checked out successfully' 11 | git.refresh() 12 | 13 | module.exports = gitCheckoutCurrentFile 14 | -------------------------------------------------------------------------------- /lib/models/git-init.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | ProjectsListView = require '../views/projects-list-view' 3 | notifier = require '../notifier' 4 | 5 | gitInit = -> 6 | currentFile = atom.workspace.getActiveTextEditor()?.getPath() 7 | if not currentFile and atom.project.getPaths().length > 1 8 | promise = new ProjectsListView().result.then (path) -> init(path) 9 | else 10 | init(atom.project.getPaths()[0]) 11 | 12 | init = (path) -> 13 | git.cmd 14 | args: ['init'] 15 | cwd: path 16 | stdout: (data) -> 17 | notifier.addSuccess data 18 | atom.project.setPaths([path]) 19 | 20 | module.exports = gitInit 21 | -------------------------------------------------------------------------------- /lib/views/output-view.coffee: -------------------------------------------------------------------------------- 1 | {$, ScrollView} = require 'atom-space-pen-views' 2 | 3 | module.exports = 4 | class OutputView extends ScrollView 5 | message: '' 6 | 7 | @content: -> 8 | @div class: 'git-plus info-view', => 9 | @pre class: 'output' 10 | 11 | initialize: -> 12 | super 13 | 14 | addLine: (line) -> 15 | @message += line 16 | 17 | reset: -> 18 | @message = '' 19 | 20 | finish: -> 21 | @panel ?= atom.workspace.addBottomPanel(item: this) 22 | @find(".output").append(@message) 23 | setTimeout => 24 | @destroy() 25 | , atom.config.get('git-plus.messageTimeout') * 1000 26 | 27 | destroy: -> 28 | @panel?.destroy() 29 | -------------------------------------------------------------------------------- /lib/models/git-log.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | LogListView = require '../views/log-list-view' 3 | ViewUriLog = 'atom://git-plus:log' 4 | 5 | amountOfCommitsToShow = -> 6 | atom.config.get('git-plus.amountOfCommitsToShow') 7 | 8 | gitLog = (repo, {onlyCurrentFile}={}) -> 9 | currentFile = repo.relativize(atom.workspace.getActiveTextEditor()?.getPath()) 10 | # opener doesn't get overwritten with a new instance of LogListView 11 | atom.workspace.addOpener (filePath) -> 12 | return new LogListView if filePath is ViewUriLog 13 | 14 | atom.workspace.open(ViewUriLog).done (view) -> 15 | if view instanceof LogListView 16 | view.setRepo repo 17 | if onlyCurrentFile 18 | view.currentFileLog(onlyCurrentFile, currentFile) 19 | else 20 | view.branchLog() 21 | 22 | module.exports = gitLog 23 | -------------------------------------------------------------------------------- /lib/views/pull-branch-list-view.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | OutputView = require './output-view' 3 | BranchListView = require './branch-list-view' 4 | 5 | module.exports = 6 | # Extension of BranchListView 7 | # Takes the name of the remote to pull from 8 | class PullBranchListView extends BranchListView 9 | initialize: (@repo, @data, @remote, @extraArgs) -> 10 | super 11 | 12 | confirmed: ({name}) -> 13 | @pull name.substring(name.indexOf('/') + 1) 14 | @cancel() 15 | 16 | pull: (remoteBranch='') -> 17 | view = new OutputView() 18 | git.cmd 19 | args: ['pull'].concat(@extraArgs, @remote, remoteBranch) 20 | cwd: @repo.getWorkingDirectory() 21 | stdout: (data) -> view.addLine(data.toString()) 22 | stderr: (data) -> view.addLine(data.toString()) 23 | exit: (code) => view.finish() 24 | -------------------------------------------------------------------------------- /spec/git-spec.coffee: -------------------------------------------------------------------------------- 1 | git = require '../lib/git' 2 | Path = require 'flavored-path' 3 | 4 | pathToRepoFile = Path.get "~/.atom/packages/git-plus/lib/git.coffee" 5 | pathToSubmoduleFile = Path.get "~/.atom/packages/git-plus/spec/foo/foo.txt" 6 | 7 | describe "Git-Plus git module", -> 8 | describe "git.getRepo", -> 9 | it "returns a promise", -> 10 | waitsForPromise -> 11 | git.getRepo().then (repo) -> 12 | expect(repo.getWorkingDirectory()).toContain 'git-plus' 13 | 14 | describe "git.dir", -> 15 | it "returns a promise", -> 16 | waitsForPromise -> 17 | git.dir().then (dir) -> 18 | expect(dir).toContain 'git-plus' 19 | 20 | describe "git.getSubmodule", -> 21 | it "returns undefined when there is no submodule", -> 22 | expect(git.getSubmodule(pathToRepoFile)).toBe undefined 23 | 24 | it "returns a submodule when given file is in a submodule of a project repo", -> 25 | expect(git.getSubmodule(pathToSubmoduleFile)).toBeTruthy() 26 | -------------------------------------------------------------------------------- /lib/views/select-stage-hunk-file-view.coffee: -------------------------------------------------------------------------------- 1 | {BufferedProcess} = require 'atom' 2 | {$$, SelectListView} = require 'atom-space-pen-views' 3 | SelectStageHunks = require './select-stage-hunks-view' 4 | git = require '../git' 5 | 6 | module.exports = 7 | class SelectStageHunkFile extends SelectListView 8 | 9 | initialize: (@repo, items) -> 10 | super 11 | @show() 12 | @setItems items 13 | @focusFilterEditor() 14 | 15 | getFilterKey: -> 'path' 16 | 17 | show: -> 18 | @panel ?= atom.workspace.addModalPanel(item: this) 19 | @panel.show() 20 | @storeFocusedElement() 21 | 22 | cancelled: -> @hide() 23 | 24 | hide: -> 25 | @panel?.destroy() 26 | 27 | viewForItem: (item) -> 28 | $$ -> 29 | @li => 30 | @div class: 'pull-right', => 31 | @span class: 'inline-block highlight', item.mode 32 | @span class: 'text-warning', item.path 33 | 34 | confirmed: ({path}) -> 35 | @cancel() 36 | git.diff(@repo, path, 37 | (data) => new SelectStageHunks(@repo, data) 38 | ) 39 | -------------------------------------------------------------------------------- /lib/models/git-remove.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | notifier = require '../notifier' 3 | RemoveListView = require '../views/remove-list-view' 4 | 5 | gitRemove = (repo, {showSelector}={}) -> 6 | currentFile = repo.relativize(atom.workspace.getActiveTextEditor()?.getPath()) 7 | 8 | if currentFile? and not showSelector 9 | if window.confirm 'Are you sure?' 10 | atom.workspace.getActivePaneItem().destroy() 11 | git.cmd 12 | args: ['rm', '-f', '--ignore-unmatch', currentFile] 13 | cwd: repo.getWorkingDirectory() 14 | stdout: (data) -> 15 | notifier.addSuccess("Removed #{prettify data}") 16 | else 17 | git.cmd 18 | args: ['rm', '-r', '-n', '--ignore-unmatch', '-f', '*'] 19 | cwd: repo.getWorkingDirectory() 20 | stdout: (data) -> new RemoveListView(repo, prettify(data)) 21 | 22 | # cut off rm '' around the filenames. 23 | prettify = (data) -> 24 | data = data.match(/rm ('.*')/g) 25 | if data 26 | for file, i in data 27 | data[i] = file.match(/rm '(.*)'/)[1] 28 | else 29 | data 30 | 31 | module.exports = gitRemove 32 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Akonwi Ngoh 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /lib/views/delete-branch-view.coffee: -------------------------------------------------------------------------------- 1 | git = require '../git' 2 | notifier = require '../notifier' 3 | BranchListView = require './branch-list-view' 4 | 5 | module.exports = 6 | # Extension of BranchListView 7 | class DeleteBranchListView extends BranchListView 8 | initialize: (@repo, @data, {@isRemote}={}) -> super 9 | 10 | confirmed: ({name}) -> 11 | if name.startsWith "*" 12 | name = name.slice(1) 13 | 14 | unless @isRemote 15 | @delete name 16 | else 17 | branch = name.substring(name.indexOf('/') + 1) 18 | remote = name.substring(0, name.indexOf('/')) 19 | @delete branch, remote 20 | 21 | @cancel() 22 | 23 | delete: (branch, remote = '') -> 24 | if remote.length is 0 25 | git.cmd 26 | args: ['branch', '-D', branch] 27 | cwd: @repo.getWorkingDirectory() 28 | stdout: (data) -> notifier.addSuccess(data.toString()) 29 | else 30 | git.cmd 31 | args: ['push', remote, '--delete', branch] 32 | cwd: @repo.getWorkingDirectory() 33 | stderr: (data) -> notifier.addSuccess(data.toString()) 34 | -------------------------------------------------------------------------------- /lib/views/repo-list-view.coffee: -------------------------------------------------------------------------------- 1 | {$$, SelectListView} = require 'atom-space-pen-views' 2 | git = require '../git' 3 | 4 | module.exports = 5 | class ListView extends SelectListView 6 | initialize: (@repos) -> 7 | super 8 | @currentPane = atom.workspace.getActivePane() 9 | @result = new Promise (resolve, reject) => 10 | @resolve = resolve 11 | @reject = reject 12 | @setup() 13 | 14 | getFilterKey: -> 'name' 15 | 16 | setup: -> 17 | @repos = @repos.map (r) -> 18 | path = r.getWorkingDirectory() 19 | return { 20 | name: path.substring(path.lastIndexOf('/')+1) 21 | repo: r 22 | } 23 | @setItems @repos 24 | @show() 25 | 26 | show: -> 27 | @filterEditorView.getModel().placeholderText = 'Which repo?' 28 | @panel ?= atom.workspace.addModalPanel(item: this) 29 | @panel.show() 30 | @focusFilterEditor() 31 | @storeFocusedElement() 32 | 33 | hide: -> @panel?.destroy() 34 | 35 | cancelled: -> @hide() 36 | 37 | viewForItem: ({name}) -> 38 | $$ -> @li(name) 39 | 40 | confirmed: ({repo}) -> 41 | @resolve repo 42 | @cancel() 43 | -------------------------------------------------------------------------------- /keymaps/git-plus.cson: -------------------------------------------------------------------------------- 1 | # Keybindings require three things to be fully defined: A selector that is 2 | # matched against the focused element, the keystroke and the command to 3 | # execute. 4 | # 5 | # Below is a basic keybinding which registers on all platforms by applying to 6 | # the root workspace element. 7 | 8 | # For more detailed documentation see 9 | # https://atom.io/docs/latest/advanced/keymaps 10 | 11 | '.platform-darwin': 12 | 'cmd-shift-h': 'git-plus:menu' 13 | 'cmd-shift-c': 'git-plus:commit' 14 | 'cmd-shift-a s': 'git-plus:status' 15 | 'cmd-shift-a a': 'git-plus:add-all-and-commit' 16 | 'cmd-shift-a p': 'git-plus:add-all-commit-and-push' 17 | 18 | '.platform-win32, .platform-linux': 19 | 'ctrl-shift-h': 'git-plus:menu' 20 | 'ctrl-shift-x': 'git-plus:commit' 21 | 'ctrl-shift-a s': 'git-plus:status' 22 | 'ctrl-shift-a a': 'git-plus:add-all-and-commit' 23 | 'ctrl-shift-a p': 'git-plus:add-all-commit-and-push' 24 | 25 | '.platform-darwin atom-text-editor': 26 | 'cmd-shift-a': 'git-plus:add' 27 | 'cmd-shift-a c': 'git-plus:add-and-commit' 28 | 29 | '.platform-win32 atom-text-editor, .platform-linux atom-text-editor': 30 | 'ctrl-shift-a': 'git-plus:add' 31 | 'ctrl-shift-a c': 'git-plus:add-and-commit' 32 | -------------------------------------------------------------------------------- /menus/git-plus.cson: -------------------------------------------------------------------------------- 1 | # See https://atom.io/docs/latest/creating-a-package#menus for more details 2 | 'context-menu': 3 | 'atom-text-editor:not(.mini)': [ 4 | { 5 | 'label': 'Git add file', 6 | 'command': 'git-plus:add' 7 | } 8 | ] 9 | 10 | 'menu': [ 11 | { 12 | 'label': 'Packages' 13 | 'submenu': [ 14 | 'label': 'Git Plus' 15 | 'submenu': [ 16 | { 'label': 'Add', 'command': 'git-plus:add' } 17 | { 'label': 'Add All', 'command': 'git-plus:add-all' } 18 | { 'label': 'Commit', 'command': 'git-plus:commit' } 19 | { 'label': 'Diff', 'command': 'git-plus:diff' } 20 | { 'label': 'Diff All', 'command': 'git-plus:diff-all' } 21 | { 'label': 'Add & Commit', 'command': 'git-plus:add-and-commit'} 22 | { 'label': 'Add All & Commit', 'command': 'git-plus:add-all-and-commit'} 23 | { 'label': 'Add All & Commit & Push', 'command': 'git-plus:add-all-commit-and-push'} 24 | { 'label': 'Push', 'command': 'git-plus:push' } 25 | { 'label': 'Log', 'command': 'git-plus:log' } 26 | { 'label': 'Merge', 'command': 'git-plus:merge'}, 27 | { 'label': 'Pull Using Rebase', 'command': 'git-plus:pull-using-rebase'} 28 | ] 29 | ] 30 | } 31 | ] 32 | -------------------------------------------------------------------------------- /lib/views/projects-list-view.coffee: -------------------------------------------------------------------------------- 1 | {$$, SelectListView} = require 'atom-space-pen-views' 2 | git = require '../git' 3 | 4 | module.exports = 5 | class ListView extends SelectListView 6 | initialize: -> 7 | super 8 | @currentPane = atom.workspace.getActivePane() 9 | @result = new Promise (resolve, reject) => 10 | @resolve = resolve 11 | @reject = reject 12 | @setup() 13 | 14 | getFilterKey: -> 'path' 15 | 16 | setup: -> 17 | @setItems atom.project.getPaths().map (p) -> 18 | return { 19 | path: p 20 | relativized: p.substring(p.lastIndexOf('/')+1) 21 | } 22 | @show() 23 | 24 | show: -> 25 | @filterEditorView.getModel().placeholderText = 'Initialize new repo where?' 26 | @panel ?= atom.workspace.addModalPanel(item: this) 27 | @panel.show() 28 | @focusFilterEditor() 29 | @storeFocusedElement() 30 | 31 | hide: -> @panel?.destroy() 32 | 33 | cancelled: -> 34 | @hide() 35 | 36 | viewForItem: ({path, relativized}) -> 37 | $$ -> 38 | @li => 39 | @div class: 'text-highlight', relativized 40 | @div class: 'text-info', path 41 | 42 | confirmed: ({path}) -> 43 | @resolve path 44 | @cancel() 45 | -------------------------------------------------------------------------------- /lib/models/git-run.coffee: -------------------------------------------------------------------------------- 1 | {CompositeDisposable} = require 'atom' 2 | {$, TextEditorView, View} = require 'atom-space-pen-views' 3 | 4 | git = require '../git' 5 | notifier = require '../notifier' 6 | 7 | class InputView extends View 8 | @content: -> 9 | @div => 10 | @subview 'commandEditor', new TextEditorView(mini: true, placeHolderText: 'Git command and arguments') 11 | 12 | initialize: (@repo) -> 13 | @disposables = new CompositeDisposable 14 | @currentPane = atom.workspace.getActivePane() 15 | @panel ?= atom.workspace.addModalPanel(item: this) 16 | @panel.show() 17 | @commandEditor.focus() 18 | 19 | @disposables.add atom.commands.add 'atom-text-editor', 'core:cancel': (e) => 20 | @panel?.destroy() 21 | @currentPane.activate() 22 | @disposables.dispose() 23 | 24 | @disposables.add atom.commands.add 'atom-text-editor', 'core:confirm', (e) => 25 | @disposables.dispose() 26 | @panel?.destroy() 27 | args = @commandEditor.getText().split(' ') 28 | if args[0] is 1 then args.shift() 29 | git.cmd 30 | args: args 31 | cwd: @repo.getWorkingDirectory() 32 | stdout: (data) => 33 | notifier.addSuccess(data.toString()) if data.toString().length > 0 34 | git.refresh() 35 | @currentPane.activate() 36 | 37 | module.exports = (repo) -> new InputView(repo) 38 | -------------------------------------------------------------------------------- /lib/views/tag-list-view.coffee: -------------------------------------------------------------------------------- 1 | {BufferedProcess} = require 'atom' 2 | {$$, SelectListView} = require 'atom-space-pen-views' 3 | 4 | TagView = require './tag-view' 5 | TagCreateView = require './tag-create-view' 6 | 7 | module.exports = 8 | class TagListView extends SelectListView 9 | 10 | initialize: (@repo, @data='') -> 11 | super 12 | @show() 13 | @parseData() 14 | 15 | parseData: -> 16 | if @data.length > 0 17 | @data = @data.split("\n")[...-1] 18 | items = ( 19 | for item in @data.reverse() when item != '' 20 | tmp = item.match /([\w\d-_/.]+)\s(.*)/ 21 | {tag: tmp?[1], annotation: tmp?[2]} 22 | ) 23 | else 24 | items = [] 25 | 26 | items.push {tag: '+ Add Tag', annotation: 'Add a tag referencing the current commit.'} 27 | @setItems items 28 | @focusFilterEditor() 29 | 30 | getFilterKey: -> 'tag' 31 | 32 | show: -> 33 | @panel ?= atom.workspace.addModalPanel(item: this) 34 | @panel.show() 35 | @storeFocusedElement() 36 | 37 | cancelled: -> @hide() 38 | 39 | hide: -> @panel?.destroy() 40 | 41 | viewForItem: ({tag, annotation}) -> 42 | $$ -> 43 | @li => 44 | @div class: 'text-highlight', tag 45 | @div class: 'text-warning', annotation 46 | 47 | confirmed: ({tag}) -> 48 | @cancel() 49 | if tag is '+ Add Tag' 50 | new TagCreateView(@repo) 51 | else 52 | new TagView(@repo, tag) 53 | -------------------------------------------------------------------------------- /lib/views/cherry-pick-select-branch-view.coffee: -------------------------------------------------------------------------------- 1 | {BufferedProcess} = require 'atom' 2 | {$$, SelectListView} = require 'atom-space-pen-views' 3 | 4 | git = require '../git' 5 | notifier = require '../notifier' 6 | CherryPickSelectCommits = require './cherry-pick-select-commits-view' 7 | 8 | module.exports = 9 | class CherryPickSelectBranch extends SelectListView 10 | 11 | initialize: (@repo, items, @currentHead) -> 12 | super 13 | @show() 14 | @setItems items 15 | @focusFilterEditor() 16 | 17 | show: -> 18 | @panel ?= atom.workspace.addModalPanel(item: this) 19 | @panel.show() 20 | 21 | @storeFocusedElement() 22 | 23 | cancelled: -> @hide() 24 | 25 | hide: -> 26 | @panel?.destroy() 27 | 28 | viewForItem: (item) -> 29 | $$ -> 30 | @li item 31 | 32 | confirmed: (item) -> 33 | @cancel() 34 | args = [ 35 | 'log' 36 | '--cherry-pick' 37 | '-z' 38 | '--format=%H%n%an%n%ar%n%s' 39 | "#{@currentHead}...#{item}" 40 | ] 41 | 42 | repo = @repo 43 | git.cmd 44 | args: args 45 | cwd: repo.getWorkingDirectory() 46 | stdout: (data) -> 47 | @save ?= '' 48 | @save += data 49 | exit: (exit) -> 50 | if exit is 0 and @save? 51 | new CherryPickSelectCommits(repo, @save.split('\0')[...-1]) 52 | @save = null 53 | else 54 | notifier.addInfo "No commits available to cherry-pick." 55 | -------------------------------------------------------------------------------- /lib/views/merge-list-view.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs-plus' 2 | {$$, SelectListView} = require 'atom-space-pen-views' 3 | git = require '../git' 4 | notifier = require '../notifier' 5 | 6 | module.exports = 7 | class ListView extends SelectListView 8 | initialize: (@repo, @data) -> 9 | super 10 | @show() 11 | @parseData() 12 | 13 | parseData: -> 14 | items = @data.split("\n") 15 | branches = [] 16 | for item in items 17 | item = item.replace(/\s/g, '') 18 | unless item is '' 19 | branches.push {name: item} 20 | @setItems branches 21 | @focusFilterEditor() 22 | 23 | getFilterKey: -> 'name' 24 | 25 | show: -> 26 | @panel ?= atom.workspace.addModalPanel(item: this) 27 | @panel.show() 28 | @storeFocusedElement() 29 | 30 | cancelled: -> @hide() 31 | 32 | hide: -> 33 | @panel?.destroy() 34 | 35 | viewForItem: ({name}) -> 36 | current = false 37 | if name.startsWith "*" 38 | name = name.slice(1) 39 | current = true 40 | $$ -> 41 | @li name, => 42 | @div class: 'pull-right', => 43 | @span('Current') if current 44 | 45 | confirmed: ({name}) -> 46 | @merge name.match(/\*?(.*)/)[1] 47 | @cancel() 48 | 49 | merge: (branch) -> 50 | git.cmd 51 | args: ['merge', branch] 52 | cwd: @repo.getWorkingDirectory() 53 | stdout: (data) => 54 | notifier.addSuccess data.toString() 55 | atom.workspace.getTextEditors().forEach (editor) -> 56 | fs.exists editor.getPath(), (exist) -> editor.destroy() if not exist 57 | git.refresh() 58 | -------------------------------------------------------------------------------- /styles/git-plus.less: -------------------------------------------------------------------------------- 1 | // The ui-variables file is provided by base themes provided by Atom. 2 | // 3 | // See https://github.com/atom/atom-dark-ui/blob/master/stylesheets/ui-variables.less 4 | // for a full listing of what's available. 5 | @import "ui-variables"; 6 | 7 | .git-plus { 8 | .error { 9 | color: @text-color-warning; 10 | } 11 | 12 | .success { 13 | color: @text-color-success; 14 | } 15 | 16 | .atom-panel.modal.from-bottom { 17 | width: 600px; 18 | } 19 | 20 | .info-view { 21 | overflow: auto; 22 | } 23 | } 24 | 25 | atom-text-editor, 26 | atom-text-editor::shadow { 27 | .meta.diff.range.unified { 28 | color: @text-color-info; 29 | } 30 | 31 | .meta.diff.header { 32 | color: @text-color-info; 33 | } 34 | 35 | .markup.deleted.diff { 36 | color: @text-color-error; 37 | } 38 | 39 | .markup.inserted.diff { 40 | color: @text-color-success; 41 | } 42 | } 43 | 44 | .git-plus-log { 45 | background-color: @base-background-color; 46 | overflow-y: auto; 47 | 48 | #git-plus-commits { 49 | color: @text-color; 50 | cursor: pointer; 51 | 52 | td { 53 | padding: 8px 10px; 54 | border-bottom: 1px solid #000; 55 | vertical-align: top; 56 | } 57 | 58 | .author { 59 | width: 30%; 60 | } 61 | 62 | .date { 63 | width: 25%; 64 | } 65 | 66 | .message { 67 | width: 50%; 68 | } 69 | 70 | .hashShort { 71 | width: 10%; 72 | text-align: right; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "git-plus", 3 | "main": "./lib/git-plus", 4 | "version": "5.4.7", 5 | "description": "Do git things without the terminal", 6 | "keywords": [ 7 | "git" 8 | ], 9 | "activationCommands": { 10 | "atom-workspace": [ 11 | "git-plus:menu", 12 | "git-plus:add", 13 | "git-plus:add-all", 14 | "git-plus:add-all-and-commit", 15 | "git-plus:add-and-commit", 16 | "git-plus:diff-all", 17 | "git-plus:diff", 18 | "git-plus:log", 19 | "git-plus:log-current-file", 20 | "git-plus:status", 21 | "git-plus:push", 22 | "git-plus:pull", 23 | "git-plus:pull-using-rebase", 24 | "git-plus:remove", 25 | "git-plus:remove-current-file", 26 | "git-plus:checkout", 27 | "git-plus:checkout-remote", 28 | "git-plus:checkout-current-file", 29 | "git-plus:checkout-all-files", 30 | "git-plus:cherry-pick", 31 | "git-plus:commit", 32 | "git-plus:commit-amend", 33 | "git-plus:fetch", 34 | "git-plus:new-branch", 35 | "git-plus:reset-head", 36 | "git-plus:show", 37 | "git-plus:stage-files", 38 | "git-plus:stage-hunk", 39 | "git-plus:stash-save", 40 | "git-plus:stash-pop", 41 | "git-plus:stash-keep", 42 | "git-plus:stash-drop", 43 | "git-plus:tags", 44 | "git-plus:unstage-files", 45 | "git-plus:init", 46 | "git-plus:run", 47 | "git-plus:merge" 48 | ] 49 | }, 50 | "repository": "https://github.com/akonwi/git-plus", 51 | "license": "MIT", 52 | "engines": { 53 | "atom": ">0.50.0" 54 | }, 55 | "dependencies": { 56 | "fs-plus": "^2.2.0", 57 | "fuzzaldrin": "^1.2.0", 58 | "underscore-plus": "1.x", 59 | "atom-space-pen-views": "^2.0.3", 60 | "flavored-path": "0.0.8" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/views/branch-list-view.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs-plus' 2 | {$$, SelectListView} = require 'atom-space-pen-views' 3 | git = require '../git' 4 | notifier = require '../notifier' 5 | 6 | module.exports = 7 | class ListView extends SelectListView 8 | args: ['checkout'] 9 | 10 | initialize: (@repo, @data) -> 11 | super 12 | @show() 13 | @parseData() 14 | @currentPane = atom.workspace.getActivePane() 15 | 16 | parseData: -> 17 | items = @data.split("\n") 18 | branches = [] 19 | for item in items 20 | item = item.replace(/\s/g, '') 21 | unless item is '' 22 | branches.push {name: item} 23 | @setItems branches 24 | @focusFilterEditor() 25 | 26 | getFilterKey: -> 'name' 27 | 28 | show: -> 29 | @panel ?= atom.workspace.addModalPanel(item: this) 30 | @panel.show() 31 | 32 | @storeFocusedElement() 33 | 34 | cancelled: -> @hide() 35 | 36 | hide: -> 37 | @panel?.destroy() 38 | 39 | viewForItem: ({name}) -> 40 | current = false 41 | if name.startsWith "*" 42 | name = name.slice(1) 43 | current = true 44 | $$ -> 45 | @li name, => 46 | @div class: 'pull-right', => 47 | @span('Current') if current 48 | 49 | confirmed: ({name}) -> 50 | @checkout name.match(/\*?(.*)/)[1] 51 | @cancel() 52 | 53 | checkout: (branch) -> 54 | git.cmd 55 | cwd: @repo.getWorkingDirectory() 56 | args: @args.concat(branch) 57 | # using `stderr` for success here 58 | stderr: (data) => 59 | notifier.addSuccess data.toString() 60 | atom.workspace.observeTextEditors (editor) => 61 | if filepath = editor.getPath()?.toString() 62 | fs.exists filepath, (exists) => 63 | editor.destroy() if not exists 64 | git.refresh() 65 | @currentPane.activate() 66 | -------------------------------------------------------------------------------- /lib/views/select-unstage-files-view.coffee: -------------------------------------------------------------------------------- 1 | {$, $$} = require 'atom-space-pen-views' 2 | 3 | git = require '../git' 4 | OutputView = require './output-view' 5 | notifier = require '../notifier' 6 | SelectListMultipleView = require './select-list-multiple-view' 7 | 8 | module.exports = 9 | class SelectStageFilesView extends SelectListMultipleView 10 | 11 | initialize: (@repo, items) -> 12 | super 13 | @show() 14 | @setItems items 15 | @focusFilterEditor() 16 | 17 | getFilterKey: -> 18 | 'path' 19 | 20 | addButtons: -> 21 | viewButton = $$ -> 22 | @div class: 'buttons', => 23 | @span class: 'pull-left', => 24 | @button class: 'btn btn-error inline-block-tight btn-cancel-button', 'Cancel' 25 | @span class: 'pull-right', => 26 | @button class: 'btn btn-success inline-block-tight btn-unstage-button', 'Unstage' 27 | viewButton.appendTo(this) 28 | 29 | @on 'click', 'button', ({target}) => 30 | @complete() if $(target).hasClass('btn-unstage-button') 31 | @cancel() if $(target).hasClass('btn-cancel-button') 32 | 33 | show: -> 34 | @panel ?= atom.workspace.addModalPanel(item: this) 35 | @panel.show() 36 | 37 | @storeFocusedElement() 38 | 39 | cancelled: -> @hide() 40 | 41 | hide: -> 42 | @panel?.destroy() 43 | 44 | viewForItem: (item, matchedStr) -> 45 | $$ -> 46 | @li => 47 | @div class: 'pull-right', => 48 | @span class: 'inline-block highlight', item.mode 49 | if matchedStr? then @raw(matchedStr) else @span item.path 50 | 51 | completed: (items) -> 52 | files = (item.path for item in items) 53 | @cancel() 54 | 55 | git.cmd 56 | args: ['reset', 'HEAD', '--'].concat(files) 57 | cwd: @repo.getWorkingDirectory() 58 | stdout: (data) => 59 | notifier.addSuccess(data) 60 | -------------------------------------------------------------------------------- /lib/views/select-stage-files-view.coffee: -------------------------------------------------------------------------------- 1 | {$, $$} = require 'atom-space-pen-views' 2 | 3 | git = require '../git' 4 | OutputView = require './output-view' 5 | notifier = require '../notifier' 6 | SelectListMultipleView = require './select-list-multiple-view' 7 | 8 | module.exports = 9 | class SelectStageFilesView extends SelectListMultipleView 10 | 11 | initialize: (@repo, items) -> 12 | super 13 | @show() 14 | @setItems items 15 | @focusFilterEditor() 16 | 17 | getFilterKey: -> 'path' 18 | 19 | addButtons: -> 20 | viewButton = $$ -> 21 | @div class: 'buttons', => 22 | @span class: 'pull-left', => 23 | @button class: 'btn btn-error inline-block-tight btn-cancel-button', 'Cancel' 24 | @span class: 'pull-right', => 25 | @button class: 'btn btn-success inline-block-tight btn-stage-button', 'Stage' 26 | viewButton.appendTo(this) 27 | 28 | @on 'click', 'button', ({target}) => 29 | @complete() if $(target).hasClass('btn-stage-button') 30 | @cancel() if $(target).hasClass('btn-cancel-button') 31 | 32 | show: -> 33 | @panel ?= atom.workspace.addModalPanel(item: this) 34 | @panel.show() 35 | @storeFocusedElement() 36 | 37 | cancelled: -> @hide() 38 | 39 | hide: -> 40 | @panel?.destroy() 41 | 42 | viewForItem: (item, matchedStr) -> 43 | $$ -> 44 | @li => 45 | @div class: 'pull-right', => 46 | @span class: 'inline-block highlight', item.mode 47 | if matchedStr? then @raw(matchedStr) else @span item.path 48 | 49 | completed: (items) -> 50 | files = (item.path for item in items) 51 | @cancel() 52 | git.cmd 53 | args: ['add', '-f'].concat(files) 54 | cwd: @repo.getWorkingDirectory() 55 | stdout: (data) => 56 | if data is '' 57 | notifier.addSuccess 'File(s) staged successfully' 58 | else 59 | notifier.addSuccess data 60 | -------------------------------------------------------------------------------- /grammars/diff.cson: -------------------------------------------------------------------------------- 1 | 'fileTypes': [ 2 | 'diff' 3 | ] 4 | 'name': 'Diff' 5 | 'patterns': [ 6 | { 7 | 'captures': 8 | '4': 9 | 'name': 'punctuation.definition.from-file.diff' 10 | '6': 11 | 'name': 'punctuation.definition.from-file.diff' 12 | '7': 13 | 'name': 'punctuation.definition.from-file.diff' 14 | 'match': '(^(((-{3}) .+)|((\\*{3}) .+))$\\n?|^(={4}) .+(?= - ))' 15 | 'name': 'meta.diff.header.from-file' 16 | } 17 | { 18 | 'captures': 19 | '2': 20 | 'name': 'punctuation.definition.to-file.diff' 21 | '3': 22 | 'name': 'punctuation.definition.to-file.diff' 23 | '4': 24 | 'name': 'punctuation.definition.to-file.diff' 25 | 'match': '(^(\\+{3}) .+$\\n?| (-) .* (={4})$\\n?)' 26 | 'name': 'meta.diff.header.to-file' 27 | } 28 | { 29 | 'captures': 30 | '1': 31 | 'name': 'punctuation.definition.range.diff' 32 | '2': 33 | 'name': 'meta.toc-list.line-number.diff' 34 | '3': 35 | 'name': 'punctuation.definition.range.diff' 36 | 'match': '^(@@)\\s*(.+?)\\s*(@@)($\\n?)?' 37 | 'name': 'meta.diff.range.unified' 38 | } 39 | { 40 | 'captures': 41 | '3': 42 | 'name': 'punctuation.definition.inserted.diff' 43 | '6': 44 | 'name': 'punctuation.definition.inserted.diff' 45 | 'match': '^(((>)( .*)?)|((\\+).*))$\\n?' 46 | 'name': 'markup.inserted.diff' 47 | } 48 | { 49 | 'captures': 50 | '3': 51 | 'name': 'punctuation.definition.inserted.diff' 52 | '6': 53 | 'name': 'punctuation.definition.inserted.diff' 54 | 'match': '^(((<)( .*)?)|((-).*))$\\n?' 55 | 'name': 'markup.deleted.diff' 56 | } 57 | { 58 | 'match': '\\[\\-.*?\\-\\]' 59 | 'name': 'markup.deleted.diff' 60 | } 61 | { 62 | 'match': '\\{\\+.*?\\+\\}' 63 | 'name': 'markup.inserted.diff' 64 | } 65 | 66 | ] 67 | 'scopeName': 'source.diff' 68 | -------------------------------------------------------------------------------- /lib/views/tag-create-view.coffee: -------------------------------------------------------------------------------- 1 | Os = require 'os' 2 | Path = require 'path' 3 | fs = require 'fs-plus' 4 | 5 | {BufferedProcess, CompositeDisposable} = require 'atom' 6 | {$, TextEditorView, View} = require 'atom-space-pen-views' 7 | notifier = require '../notifier' 8 | git = require '../git' 9 | 10 | module.exports= 11 | class TagCreateView extends View 12 | @content: -> 13 | @div => 14 | @div class: 'block', => 15 | @subview 'tagName', new TextEditorView(mini: true, placeholderText: 'Tag') 16 | @div class: 'block', => 17 | @subview 'tagMessage', new TextEditorView(mini: true, placeholderText: 'Annotation message') 18 | @div class: 'block', => 19 | @span class: 'pull-left', => 20 | @button class: 'btn btn-success inline-block-tight gp-confirm-button', click: 'createTag', 'Create Tag' 21 | @span class: 'pull-right', => 22 | @button class: 'btn btn-error inline-block-tight gp-cancel-button', click: 'destroy', 'Cancel' 23 | 24 | initialize: (@repo) -> 25 | @disposables = new CompositeDisposable 26 | @currentPane = atom.workspace.getActivePane() 27 | @panel ?= atom.workspace.addModalPanel(item: this) 28 | @panel.show() 29 | @tagName.focus() 30 | @disposables.add atom.commands.add 'atom-text-editor', 'core:cancel': => @destroy() 31 | @disposables.add atom.commands.add 'atom-text-editor', 'core:confirm': => @createTag() 32 | 33 | createTag: -> 34 | tag = name: @tagName.getModel().getText(), message: @tagMessage.getModel().getText() 35 | git.cmd 36 | args: ['tag', '-a', tag.name, '-m', tag.message] 37 | cwd: @repo.getWorkingDirectory() 38 | stderr: (data) -> 39 | notifier.addError(data.toString()) 40 | exit: (code) -> 41 | notifier.addSuccess("Tag '#{tag.name}' has been created successfully!") if code is 0 42 | @destroy() 43 | 44 | destroy: -> 45 | @panel?.destroy() 46 | @disposables.dispose() 47 | @currentPane.activate() 48 | -------------------------------------------------------------------------------- /lib/models/git-branch.coffee: -------------------------------------------------------------------------------- 1 | {CompositeDisposable} = require 'atom' 2 | {$, TextEditorView, View} = require 'atom-space-pen-views' 3 | 4 | git = require '../git' 5 | notifier = require '../notifier' 6 | BranchListView = require '../views/branch-list-view' 7 | RemoteBranchListView = require '../views/remote-branch-list-view' 8 | 9 | class InputView extends View 10 | @content: -> 11 | @div => 12 | @subview 'branchEditor', new TextEditorView(mini: true, placeholderText: 'New branch name') 13 | 14 | initialize: (@repo) -> 15 | @disposables = new CompositeDisposable 16 | @currentPane = atom.workspace.getActivePane() 17 | panel = atom.workspace.addModalPanel(item: this) 18 | panel.show() 19 | 20 | destroy = => 21 | panel.destroy() 22 | @disposables.dispose() 23 | @currentPane.activate() 24 | 25 | @branchEditor.focus() 26 | @disposables.add atom.commands.add 'atom-text-editor', 'core:cancel': (event) -> destroy() 27 | @disposables.add atom.commands.add 'atom-text-editor', 'core:confirm': (event) => 28 | editor = @branchEditor.getModel() 29 | name = editor.getText() 30 | if name.length > 0 31 | @createBranch name 32 | destroy() 33 | 34 | createBranch: (name) -> 35 | git.cmd 36 | args: ['checkout', '-b', name] 37 | cwd: @repo.getWorkingDirectory() 38 | # using `stderr` for success 39 | stderr: (data) => 40 | notifier.addSuccess data.toString() 41 | git.refresh() 42 | @currentPane.activate() 43 | 44 | module.exports.newBranch = (repo) -> 45 | new InputView(repo) 46 | 47 | module.exports.gitBranches = (repo) -> 48 | git.cmd 49 | args: ['branch'] 50 | cwd: repo.getWorkingDirectory() 51 | stdout: (data) -> 52 | new BranchListView(repo, data) 53 | 54 | module.exports.gitRemoteBranches = (repo) -> 55 | git.cmd 56 | args: ['branch', '-r'] 57 | cwd: repo.getWorkingDirectory() 58 | stdout: (data) -> 59 | new RemoteBranchListView(repo, data) 60 | -------------------------------------------------------------------------------- /lib/views/tag-view.coffee: -------------------------------------------------------------------------------- 1 | {$$, SelectListView} = require 'atom-space-pen-views' 2 | 3 | git = require '../git' 4 | GitShow = require '../models/git-show' 5 | notifier = require '../notifier' 6 | RemoteListView = require '../views/remote-list-view' 7 | 8 | module.exports = 9 | class TagView extends SelectListView 10 | initialize: (@repo, @tag) -> 11 | super 12 | @show() 13 | @parseData() 14 | 15 | parseData: -> 16 | items = [] 17 | items.push {tag: @tag, cmd: 'Show', description: 'git show'} 18 | items.push {tag: @tag, cmd: 'Push', description: 'git push [remote]'} 19 | items.push {tag: @tag, cmd: 'Checkout', description: 'git checkout'} 20 | items.push {tag: @tag, cmd: 'Verify', description: 'git tag --verify'} 21 | items.push {tag: @tag, cmd: 'Delete', description: 'git tag --delete'} 22 | 23 | @setItems items 24 | @focusFilterEditor() 25 | 26 | show: -> 27 | @panel ?= atom.workspace.addModalPanel(item: this) 28 | @panel.show() 29 | @storeFocusedElement() 30 | 31 | cancelled: -> @hide() 32 | 33 | hide: -> @panel?.destroy() 34 | 35 | viewForItem: ({tag, cmd, description}) -> 36 | $$ -> 37 | @li => 38 | @div class: 'text-highlight', cmd 39 | @div class: 'text-warning', "#{description} #{tag}" 40 | 41 | getFilterKey: -> 'cmd' 42 | 43 | confirmed: ({tag, cmd}) -> 44 | @cancel() 45 | switch cmd 46 | when 'Show' 47 | GitShow(@repo, tag) 48 | return 49 | when 'Push' 50 | git.cmd 51 | args: ['remote'] 52 | cwd: @repo.getWorkingDirectory() 53 | stdout: (data) => new RemoteListView(@repo, data, mode: 'push', tag: @tag) 54 | return 55 | when 'Checkout' 56 | args = ['checkout', tag] 57 | when 'Verify' 58 | args = ['tag', '--verify', tag] 59 | when 'Delete' 60 | args = ['tag', '--delete', tag] 61 | 62 | git.cmd 63 | args: args 64 | cwd: @repo.getWorkingDirectory() 65 | stdout: (data) -> notifier.addSuccess(data.toString()) 66 | -------------------------------------------------------------------------------- /lib/views/cherry-pick-select-commits-view.coffee: -------------------------------------------------------------------------------- 1 | {$, $$} = require 'atom-space-pen-views' 2 | 3 | git = require '../git' 4 | OutputView = require './output-view' 5 | notifier = require '../notifier' 6 | SelectListMultipleView = require './select-list-multiple-view' 7 | 8 | module.exports = 9 | class CherryPickSelectCommits extends SelectListMultipleView 10 | 11 | initialize: (@repo, data) -> 12 | super 13 | @show() 14 | @setItems( 15 | for item in data 16 | item = item.split('\n') 17 | {hash: item[0], author: item[1], time: item[2], subject: item[3]} 18 | ) 19 | @focusFilterEditor() 20 | 21 | getFilterKey: -> 22 | 'hash' 23 | 24 | addButtons: -> 25 | viewButton = $$ -> 26 | @div class: 'buttons', => 27 | @span class: 'pull-left', => 28 | @button class: 'btn btn-error inline-block-tight btn-cancel-button', 'Cancel' 29 | @span class: 'pull-right', => 30 | @button class: 'btn btn-success inline-block-tight btn-pick-button', 'Cherry-Pick!' 31 | viewButton.appendTo(this) 32 | 33 | @on 'click', 'button', ({target}) => 34 | @complete() if $(target).hasClass('btn-pick-button') 35 | @cancel() if $(target).hasClass('btn-cancel-button') 36 | 37 | show: -> 38 | @panel ?= atom.workspace.addModalPanel(item: this) 39 | @panel.show() 40 | 41 | @storeFocusedElement() 42 | 43 | cancelled: -> 44 | @hide() 45 | 46 | hide: -> 47 | @panel?.destroy() 48 | 49 | viewForItem: (item, matchedStr) -> 50 | $$ -> 51 | @li => 52 | @div class: 'text-highlight inline-block pull-right', style: 'font-family: monospace', => 53 | if matchedStr? then @raw(matchedStr) else @span item.hash 54 | @div class: 'text-info', "#{item.author}, #{item.time}" 55 | @div class: 'text-warning', item.subject 56 | 57 | completed: (items) -> 58 | @cancel() 59 | commits = (item.hash for item in items) 60 | git.cmd 61 | args: ['cherry-pick'].concat(commits) 62 | cwd: @repo.getWorkingDirectory() 63 | stdout: (data) => 64 | notifier.addSuccess data 65 | -------------------------------------------------------------------------------- /lib/views/remove-list-view.coffee: -------------------------------------------------------------------------------- 1 | {$, $$, EditorView} = require 'atom-space-pen-views' 2 | 3 | git = require '../git' 4 | OutputView = require './output-view' 5 | notifier = require '../notifier' 6 | SelectListMultipleView = require './select-list-multiple-view' 7 | 8 | module.exports = 9 | class SelectStageFilesView extends SelectListMultipleView 10 | 11 | initialize: (@repo, items) -> 12 | super 13 | @show() 14 | @setItems items 15 | @focusFilterEditor() 16 | 17 | addButtons: -> 18 | viewButton = $$ -> 19 | @div class: 'buttons', => 20 | @span class: 'pull-left', => 21 | @button class: 'btn btn-error inline-block-tight btn-cancel-button', 'Cancel' 22 | @span class: 'pull-right', => 23 | @button class: 'btn btn-success inline-block-tight btn-remove-button', 'Remove' 24 | viewButton.appendTo(this) 25 | 26 | @on 'click', 'button', ({target}) => 27 | if $(target).hasClass('btn-remove-button') 28 | @complete() if window.confirm 'Are you sure?' 29 | @cancel() if $(target).hasClass('btn-cancel-button') 30 | 31 | show: -> 32 | @panel ?= atom.workspace.addModalPanel(item: this) 33 | @panel.show() 34 | @storeFocusedElement() 35 | 36 | cancelled: -> 37 | @hide() 38 | 39 | hide: -> 40 | @panel?.destroy() 41 | 42 | viewForItem: (item, matchedStr) -> 43 | $$ -> 44 | @li => 45 | if matchedStr? then @raw(matchedStr) else @span item 46 | 47 | completed: (items) -> 48 | files = (item for item in items when item isnt '') 49 | @cancel() 50 | currentFile = @repo.relativize atom.workspace.getActiveTextEditor()?.getPath() 51 | 52 | editor = atom.workspace.getActiveTextEditor() 53 | atom.views.getView(editor).remove() if currentFile in files 54 | git.cmd 55 | args: ['rm', '-f'].concat(files) 56 | cwd: @repo.getWorkingDirectory() 57 | stdout: (data) -> 58 | notifier.addSuccess "Removed #{prettify data}" 59 | 60 | # cut off rm '' around the filenames. 61 | prettify = (data) -> 62 | data = data.match(/rm ('.*')/g) 63 | if data?.length >= 1 64 | for file, i in data 65 | data[i] = ' ' + file.match(/rm '(.*)'/)[1] 66 | -------------------------------------------------------------------------------- /lib/views/status-list-view.coffee: -------------------------------------------------------------------------------- 1 | {$$, SelectListView} = require 'atom-space-pen-views' 2 | fs = require 'fs' 3 | Path = require 'path' 4 | git = require '../git' 5 | GitDiff = require '../models/git-diff' 6 | notifier = require '../notifier' 7 | 8 | module.exports = 9 | class StatusListView extends SelectListView 10 | initialize: (@repo, @data, {@onlyCurrentFile}={}) -> 11 | super 12 | @show() 13 | @branch = @data[0] 14 | @setItems @parseData @data[...-1] 15 | @focusFilterEditor() 16 | 17 | parseData: (files) -> 18 | for line in files when /^([ MADRCU?!]{2})\s{1}(.*)/.test line 19 | line = line.match /^([ MADRCU?!]{2})\s{1}(.*)/ 20 | {type: line[1], path: line[2]} 21 | 22 | getFilterKey: -> 'path' 23 | 24 | show: -> 25 | @panel ?= atom.workspace.addModalPanel(item: this) 26 | @panel.show() 27 | @storeFocusedElement() 28 | 29 | cancelled: -> @hide() 30 | 31 | hide: -> @panel?.destroy() 32 | 33 | viewForItem: ({type, path}) -> 34 | getIcon = (s) -> 35 | return 'status-added icon icon-diff-added' if s[0] is 'A' 36 | return 'status-removed icon icon-diff-removed' if s[0] is 'D' 37 | return 'status-renamed icon icon-diff-renamed' if s[0] is 'R' 38 | return 'status-modified icon icon-diff-modified' if s[0] is 'M' or s[1] is 'M' 39 | return '' 40 | 41 | $$ -> 42 | @li => 43 | @div 44 | class: 'pull-right highlight' 45 | style: 'white-space: pre-wrap; font-family: monospace' 46 | type 47 | @span class: getIcon(type) 48 | @span path 49 | 50 | confirmed: ({type, path}) -> 51 | @cancel() 52 | if type is '??' 53 | git.add @repo, file: path 54 | else 55 | openFile = confirm("Open #{path}?") 56 | fullPath = Path.join(@repo.getWorkingDirectory(), path) 57 | 58 | fs.stat fullPath, (err, stat) => 59 | if err 60 | notifier.addError(err.message) 61 | else 62 | isDirectory = stat?.isDirectory() 63 | if openFile 64 | if isDirectory 65 | atom.open(pathsToOpen: fullPath, newWindow: true) 66 | else 67 | atom.workspace.open(fullPath) 68 | else 69 | GitDiff(@repo, file: path) 70 | -------------------------------------------------------------------------------- /lib/models/git-diff.coffee: -------------------------------------------------------------------------------- 1 | {CompositeDisposable} = require 'atom' 2 | Os = require 'os' 3 | Path = require 'path' 4 | fs = require 'fs-plus' 5 | 6 | git = require '../git' 7 | notifier = require '../notifier' 8 | 9 | disposables = new CompositeDisposable 10 | diffFilePath = null 11 | 12 | gitDiff = (repo, {diffStat, file}={}) -> 13 | diffFilePath = Path.join(repo.getPath(), "atom_git_plus.diff") 14 | file ?= repo.relativize(atom.workspace.getActiveTextEditor()?.getPath()) 15 | if not file 16 | return notifier.addError "No open file. Select 'Diff All'." 17 | diffStat ?= '' 18 | args = ['diff', '--color=never'] 19 | args.push 'HEAD' if atom.config.get 'git-plus.includeStagedDiff' 20 | args.push '--word-diff' if atom.config.get 'git-plus.wordDiff' 21 | args.push file if diffStat is '' 22 | git.cmd 23 | args: args 24 | cwd: repo.getWorkingDirectory() 25 | stdout: (data) -> diffStat += data 26 | exit: (code) -> prepFile diffStat if code is 0 27 | 28 | prepFile = (text) -> 29 | if text?.length > 0 30 | fs.writeFileSync diffFilePath, text, flag: 'w+' 31 | showFile() 32 | else 33 | notifier.addInfo 'Nothing to show.' 34 | 35 | showFile = -> 36 | atom.workspace 37 | .open(diffFilePath, searchAllPanes: true) 38 | .done (textEditor) -> 39 | if atom.config.get('git-plus.openInPane') 40 | splitPane(atom.config.get('git-plus.splitPane'), textEditor) 41 | else 42 | disposables.add textEditor.onDidDestroy => 43 | fs.unlink diffFilePath 44 | 45 | splitPane = (splitDir, oldEditor) -> 46 | pane = atom.workspace.paneForURI(diffFilePath) 47 | options = { copyActiveItem: true } 48 | hookEvents = (textEditor) -> 49 | oldEditor.destroy() 50 | disposables.add textEditor.onDidDestroy => 51 | fs.unlink diffFilePath 52 | 53 | directions = 54 | left: => 55 | pane = pane.splitLeft options 56 | hookEvents(pane.getActiveEditor()) 57 | right: => 58 | pane = pane.splitRight options 59 | hookEvents(pane.getActiveEditor()) 60 | up: => 61 | pane = pane.splitUp options 62 | hookEvents(pane.getActiveEditor()) 63 | down: => 64 | pane = pane.splitDown options 65 | hookEvents(pane.getActiveEditor()) 66 | directions[splitDir]() 67 | oldEditor.destroy() 68 | 69 | module.exports = gitDiff 70 | -------------------------------------------------------------------------------- /lib/views/remote-list-view.coffee: -------------------------------------------------------------------------------- 1 | {BufferedProcess} = require 'atom' 2 | {$$, SelectListView} = require 'atom-space-pen-views' 3 | 4 | git = require '../git' 5 | OutputView = require './output-view' 6 | PullBranchListView = require './pull-branch-list-view' 7 | 8 | module.exports = 9 | class ListView extends SelectListView 10 | initialize: (@repo, @data, {@mode, @tag, @extraArgs}) -> 11 | super 12 | @tag ?= '' 13 | @extraArgs ?= [] 14 | @show() 15 | @parseData() 16 | 17 | parseData: -> 18 | items = @data.split("\n") 19 | remotes = [] 20 | for item in items 21 | remotes.push {name: item} unless item is '' 22 | if remotes.length is 1 23 | @confirmed remotes[0] 24 | else 25 | @setItems remotes 26 | @focusFilterEditor() 27 | 28 | getFilterKey: -> 'name' 29 | 30 | show: -> 31 | @panel ?= atom.workspace.addModalPanel(item: this) 32 | @panel.show() 33 | 34 | @storeFocusedElement() 35 | 36 | cancelled: -> @hide() 37 | 38 | hide: -> 39 | @panel?.destroy() 40 | 41 | viewForItem: ({name}) -> 42 | $$ -> 43 | @li name 44 | 45 | confirmed: ({name}) -> 46 | if @mode is 'pull' 47 | git.cmd 48 | args: ['branch', '-r'], 49 | cwd: @repo.getWorkingDirectory() 50 | stdout: (data) => new PullBranchListView(@repo, data, name, @extraArgs) 51 | else if @mode is 'fetch-prune' 52 | @mode = 'fetch' 53 | @execute name, '--prune' 54 | else 55 | @execute name 56 | @cancel() 57 | 58 | execute: (remote, extraArgs='') -> 59 | view = new OutputView() 60 | args = [@mode] 61 | if extraArgs.length > 0 62 | args.push extraArgs 63 | args = args.concat([remote, @tag]) 64 | git.cmd 65 | args: args 66 | cwd: @repo.getWorkingDirectory() 67 | stdout: (data) -> view.addLine(data.toString()) 68 | stderr: (data) -> view.addLine(data.toString()) 69 | exit: (code) => 70 | if code is 128 71 | view.reset() 72 | git.cmd 73 | args: [@mode, '-u', remote, 'HEAD'] 74 | cwd: @repo.getWorkingDirectory() 75 | stdout: (data) -> view.addLine(data.toString()) 76 | stderr: (data) -> view.addLine(data.toString()) 77 | exit: (code) -> view.finish() 78 | else 79 | view.finish() 80 | -------------------------------------------------------------------------------- /lib/models/git-show.coffee: -------------------------------------------------------------------------------- 1 | Os = require 'os' 2 | Path = require 'path' 3 | fs = require 'fs-plus' 4 | 5 | {CompositeDisposable} = require 'atom' 6 | {$, TextEditorView, View} = require 'atom-space-pen-views' 7 | 8 | git = require '../git' 9 | 10 | showCommitFilePath = (objectHash) -> 11 | Path.join Os.tmpDir(), "#{objectHash}.diff" 12 | 13 | showObject = (repo, objectHash, file) -> 14 | args = ['show'] 15 | args.push '--format=full' 16 | args.push '--word-diff' if atom.config.get 'git-plus.wordDiff' 17 | args.push objectHash 18 | if file? 19 | args.push '--' 20 | args.push file 21 | 22 | git.cmd 23 | args: args 24 | cwd: repo.getWorkingDirectory() 25 | stdout: (data) -> prepFile(data, objectHash) if data.length > 0 26 | 27 | prepFile = (text, objectHash) -> 28 | fs.writeFileSync showCommitFilePath(objectHash), text, flag: 'w+' 29 | showFile(objectHash) 30 | 31 | showFile = (objectHash) -> 32 | disposables = new CompositeDisposable 33 | split = if atom.config.get('git-plus.openInPane') then atom.config.get('git-plus.splitPane') 34 | atom.workspace 35 | .open(showCommitFilePath(objectHash), split: split, activatePane: true) 36 | .done (textBuffer) => 37 | if textBuffer? 38 | disposables.add textBuffer.onDidDestroy => 39 | disposables.dispose() 40 | try fs.unlinkSync showCommitFilePath(objectHash) 41 | 42 | class InputView extends View 43 | @content: -> 44 | @div => 45 | @subview 'objectHash', new TextEditorView(mini: true, placeholderText: 'Commit hash to show') 46 | 47 | initialize: (@repo) -> 48 | @disposables = new CompositeDisposable 49 | @currentPane = atom.workspace.getActivePane() 50 | @panel ?= atom.workspace.addModalPanel(item: this) 51 | @panel.show() 52 | @objectHash.focus() 53 | @disposables.add atom.commands.add 'atom-text-editor', 'core:cancel': => @destroy() 54 | @disposables.add atom.commands.add 'atom-text-editor', 'core:confirm': => 55 | text = @objectHash.getModel().getText().split(' ') 56 | name = if text.length is 2 then text[1] else text[0] 57 | showObject(@repo, text) 58 | @destroy() 59 | 60 | destroy: -> 61 | @disposables?.dispose() 62 | @panel?.destroy() 63 | 64 | module.exports = (repo, objectHash, file) -> 65 | if not objectHash? 66 | new InputView(repo) 67 | else 68 | showObject(repo, objectHash, file) 69 | -------------------------------------------------------------------------------- /lib/views/select-stage-hunks-view.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs-plus' 2 | {$, $$} = require 'atom-space-pen-views' 3 | 4 | git = require '../git' 5 | OutputView = require './output-view' 6 | notifier = require '../notifier' 7 | SelectListMultipleView = require './select-list-multiple-view' 8 | 9 | module.exports = 10 | class SelectStageHunks extends SelectListMultipleView 11 | initialize: (@repo, data) -> 12 | super 13 | @patch_header = data[0] 14 | return @completed @_generateObjects(data[1..]) if data.length is 2 15 | @show() 16 | @setItems @_generateObjects(data[1..]) 17 | @focusFilterEditor() 18 | 19 | getFilterKey: -> 20 | 'pos' 21 | 22 | addButtons: -> 23 | viewButton = $$ -> 24 | @div class: 'buttons', => 25 | @span class: 'pull-left', => 26 | @button class: 'btn btn-error inline-block-tight btn-cancel-button', 'Cancel' 27 | @span class: 'pull-right', => 28 | @button class: 'btn btn-success inline-block-tight btn-stage-button', 'Stage' 29 | viewButton.appendTo(this) 30 | 31 | @on 'click', 'button', ({target}) => 32 | @complete() if $(target).hasClass('btn-stage-button') 33 | @cancel() if $(target).hasClass('btn-cancel-button') 34 | 35 | show: -> 36 | @panel ?= atom.workspace.addModalPanel(item: this) 37 | @panel.show() 38 | @storeFocusedElement() 39 | 40 | cancelled: -> @hide() 41 | 42 | hide: -> @panel?.destroy() 43 | 44 | viewForItem: (item, matchedStr) -> 45 | viewItem = $$ -> 46 | @li => 47 | @div class: 'inline-block highlight', => 48 | if matchedStr? then @raw(matchedStr) else @span item.pos 49 | @div class: 'text-warning gp-item-diff', style: 'white-space: pre-wrap; font-family: monospace', item.diff 50 | 51 | completed: (items) -> 52 | @cancel() 53 | return if items.length < 1 54 | 55 | patch_full = @patch_header 56 | patch_full += patch for {patch} in items 57 | 58 | patchPath = @repo.getWorkingDirectory() + '/GITPLUS_PATCH' 59 | fs.writeFileSync patchPath, patch_full, flag: 'w+' 60 | git.cmd 61 | args: ['apply', '--cached', '--', patchPath] 62 | cwd: @repo.getWorkingDirectory() 63 | stdout: (data) => 64 | data = if data? and data isnt '' then data else 'Hunk has been staged!' 65 | notifier.addSuccess(data) 66 | try fs.unlink patchPath 67 | 68 | _generateObjects: (data) -> 69 | for hunk in data when hunk isnt '' 70 | hunkSplit = hunk.match /(@@[ \-\+\,0-9]*@@.*)\n([\s\S]*)/ 71 | { 72 | pos: hunkSplit[1] 73 | diff: hunkSplit[2] 74 | patch: hunk 75 | } 76 | -------------------------------------------------------------------------------- /lib/views/git-palette-view.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'underscore-plus' 2 | {$, $$, SelectListView} = require 'atom-space-pen-views' 3 | git = require '../git' 4 | GitPlusCommands = require '../git-plus-commands' 5 | GitInit = require '../models/git-init' 6 | fuzzy = require('../models/fuzzy').filter 7 | 8 | module.exports = 9 | class GitPaletteView extends SelectListView 10 | 11 | initialize: -> 12 | super 13 | @addClass('git-palette') 14 | @toggle() 15 | 16 | getFilterKey: -> 17 | 'description' 18 | 19 | cancelled: -> @hide() 20 | 21 | toggle: -> 22 | if @panel?.isVisible() 23 | @cancel() 24 | else 25 | @show() 26 | 27 | show: -> 28 | @panel ?= atom.workspace.addModalPanel(item: this) 29 | 30 | @storeFocusedElement() 31 | 32 | if @previouslyFocusedElement[0] and @previouslyFocusedElement[0] isnt document.body 33 | @commandElement = @previouslyFocusedElement 34 | else 35 | @commandElement = atom.views.getView(atom.workspace) 36 | @keyBindings = atom.keymaps.findKeyBindings(target: @commandElement[0]) 37 | 38 | GitPlusCommands() 39 | .then (commands) => 40 | commands = commands.map (c) -> { name: c[0], description: c[1], func: c[2] } 41 | commands = _.sortBy(commands, 'name') 42 | @setItems(commands) 43 | @panel.show() 44 | @focusFilterEditor() 45 | .catch (err) => 46 | (commands = []).push { name: 'git-plus:init', description: 'Init', func: -> GitInit() } 47 | @setItems(commands) 48 | @panel.show() 49 | @focusFilterEditor() 50 | 51 | populateList: -> 52 | return unless @items? 53 | 54 | filterQuery = @getFilterQuery() 55 | if filterQuery.length 56 | options = 57 | pre: '' 58 | post: "" 59 | extract: (el) => if @getFilterKey()? then el[@getFilterKey()] else el 60 | filteredItems = fuzzy(filterQuery, @items, options) 61 | else 62 | filteredItems = @items 63 | 64 | @list.empty() 65 | if filteredItems.length 66 | @setError(null) 67 | for i in [0...Math.min(filteredItems.length, @maxItems)] 68 | item = filteredItems[i].original ? filteredItems[i] 69 | itemView = $(@viewForItem(item, filteredItems[i].string ? null)) 70 | itemView.data('select-list-item', item) 71 | @list.append(itemView) 72 | 73 | @selectItemView(@list.find('li:first')) 74 | else 75 | @setError(@getEmptyMessage(@items.length, filteredItems.length)) 76 | 77 | hide: -> 78 | @panel?.destroy() 79 | 80 | viewForItem: ({name, description}, matchedStr) -> 81 | $$ -> 82 | @li class: 'command', 'data-command-name': name, => 83 | if matchedStr? then @raw(matchedStr) else @span description 84 | 85 | confirmed: ({func}) -> 86 | @cancel() 87 | func() 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Git-Plus package 2 | 3 | [](https://coderwall.com/akonwi) 4 | 5 | vim-fugitive like package for atom. make commits and other git things without the terminal 6 | 7 |  8 | 9 | ## Usage 10 | 11 | # IMPORTANT: Make sure your gitconfig file is configured or at least your `user.email` and `user.name` variables are initialized 12 | 13 | ### Git-Plus Palette 14 | >- `Cmd-Shift-H` on MacOS 15 | >- `Ctrl-Shift-H` on Windows + Linux 16 | >- `Git Plus: Menu` on the atom command palette. 17 | 18 | ### Commands 19 | _Commands are accessible for keybindings by dasherizing the command title._ 20 | > Git Add == `git-plus:add` 21 | 22 | > Git Add All Commit And Push == `git-plus:add-all-commit-and-push` 23 | 24 | __Note: This list is not exclusive__ 25 | 1. `Git add [all]` 26 | 27 | Git add will add the current file and 'add all' will add all changed files 28 | Default key binding: `Cmd-Shift-A` 29 | 30 | 2. `Git add all commit and push` 31 | 32 | `Cmd-Shift-A P` 33 | 34 | 3. `Git commit` 35 | 36 | Will pull up a commit message file. The commit will be made when the file is saved NOT when the pane is closed. You can just cancel by closing the tab. 37 | Default key binding: `Cmd-Shift-C`(*`Ctrl-Shift-X`* on Windows + Linux) 38 | 39 | 4. `Git commit amend` 40 | 41 | Will amend the changes to previous commit. 42 | 43 | 5. `Git checkout current file` 44 | 45 | Undo changes and checkout current file. 46 | 47 | 6. `Git [checkout]` 48 | 49 | Change branches 50 | 51 | 7. `Git Diff [All]` 52 | 53 | Shows diff for current file or All the files. Diff can either be with staged or unstaged as selected in options. 54 | 55 | 8. `Git new branch` 56 | 57 | Create a new branch 58 | 59 | 9. `Git [push|pull]` 60 | 61 | When Pushing, if you have multiple remote repos, you can choose which to push to. 62 | 10. `Git Add and Commit` 63 | 64 | Add the current file and pull up the commit message file. Similar to `Git add` and `Git commit` in succession. 65 | Default key binding: `Cmd-Shift-A c` 66 | 67 | 11. `Git Add All and Commit` 68 | 69 | Add all changed files and pull up the commit message file. Similar to `Git add all` and `Git commit` in succession. 70 | Default key binding: `Cmd-Shift-A a` 71 | 72 | 12. `Git rm [current file]` 73 | 74 | Git rm the current file or open an selector to select the files to remove. You can select multiple files at once. 75 | 76 | 13. `Git Log [Current File]` 77 | 78 | Show the commit history [for the current file] and show display the selected commit. 79 | 80 | 13. `Git Show` 81 | 82 | Show the specified object, for example `HEAD`, `HEAD~2`, `3925a0d`, `origin/master` or `v2.7.3`. 83 | 84 | ## Contributing 85 | 86 | 1. Fork it 87 | 2. Create your feature branch (`git checkout -b my-new-feature`) 88 | 3. Commit your changes (`git commit -am 'Add some feature'`) 89 | 4. Push to the branch (`git push origin my-new-feature`) 90 | 5. Create new Pull Request 91 | -------------------------------------------------------------------------------- /lib/views/log-list-view.coffee: -------------------------------------------------------------------------------- 1 | {Disposable} = require 'atom' 2 | {BufferedProcess} = require 'atom' 3 | {$, $$$, ScrollView} = require 'atom-space-pen-views' 4 | git = require '../git' 5 | GitShow = require '../models/git-show' 6 | 7 | amountOfCommitsToShow = -> 8 | atom.config.get('git-plus.amountOfCommitsToShow') 9 | 10 | module.exports = 11 | class LogListView extends ScrollView 12 | @content: -> 13 | @div class: 'git-plus-log native-key-bindings', tabindex: -1, => 14 | @table id: 'git-plus-commits', outlet: 'commitsListView' 15 | 16 | onDidChangeTitle: -> new Disposable 17 | onDidChangeModified: -> new Disposable 18 | 19 | getURI: -> 'atom://git-plus:log' 20 | 21 | getTitle: -> 'git-plus: Log' 22 | 23 | initialize: -> 24 | super 25 | @skipCommits = 0 26 | @on 'click', '.commit-row', ({currentTarget}) => 27 | @showCommitLog currentTarget.getAttribute('hash') 28 | @scroll => 29 | @getLog() if @scrollTop() + @height() is @prop('scrollHeight') 30 | 31 | setRepo: (@repo) -> 32 | 33 | parseData: (data) -> 34 | if data.length > 0 35 | separator = ';|' 36 | newline = '_.;._' 37 | data = data.substring(0, data.length - newline.length - 1) 38 | 39 | commits = data.split(newline).map (line) -> 40 | if line.trim() isnt '' 41 | tmpData = line.trim().split(separator) 42 | return { 43 | hashShort: tmpData[0] 44 | hash: tmpData[1] 45 | author: tmpData[2] 46 | email: tmpData[3] 47 | message: tmpData[4] 48 | date: tmpData[5] 49 | } 50 | 51 | @renderLog commits 52 | 53 | renderHeader: -> 54 | headerRow = $$$ -> 55 | @tr class: 'commit-header', => 56 | @td 'Date' 57 | @td 'Message' 58 | @td class: 'hashShort', 'Short Hash' 59 | 60 | @commitsListView.append(headerRow) 61 | 62 | renderLog: (commits) -> 63 | commits.forEach (commit) => @renderCommit commit 64 | @skipCommits += amountOfCommitsToShow() 65 | 66 | renderCommit: (commit) -> 67 | commitRow = $$$ -> 68 | @tr class: 'commit-row', hash: "#{commit.hash}", => 69 | @td class: 'date', "#{commit.date} by #{commit.author}" 70 | @td class: 'message', "#{commit.message}" 71 | @td class: 'hashShort', "#{commit.hashShort}" 72 | 73 | @commitsListView.append(commitRow) 74 | 75 | showCommitLog: (hash) -> 76 | GitShow(@repo, hash, @currentFile if @onlyCurrentFile) 77 | 78 | branchLog: -> 79 | @skipCommits = 0 80 | @commitsListView.empty() 81 | @onlyCurrentFile = false 82 | @currentFile = null 83 | @renderHeader() 84 | @getLog() 85 | 86 | currentFileLog: (@onlyCurrentFile, @currentFile) -> 87 | @skipCommits = 0 88 | @commitsListView.empty() 89 | @renderHeader() 90 | @getLog() 91 | 92 | getLog: -> 93 | args = ['log', "--pretty=%h;|%H;|%aN;|%aE;|%s;|%ai_.;._", "-#{amountOfCommitsToShow()}", '--skip=' + @skipCommits] 94 | args.push @currentFile if @onlyCurrentFile and @currentFile? 95 | git.cmd 96 | args: args 97 | cwd: @repo.getWorkingDirectory() 98 | stdout: (data) => 99 | @parseData data 100 | -------------------------------------------------------------------------------- /lib/models/fuzzy.coffee: -------------------------------------------------------------------------------- 1 | # Fuzzy 2 | # https://github.com/myork/fuzzy 3 | # 4 | # Copyright (c) 2012 Matt York 5 | # Licensed under the MIT license. 6 | 7 | fuzzy = {} 8 | module.exports = fuzzy 9 | 10 | # Return all elements of `array` that have a fuzzy 11 | # match against `pattern`. 12 | fuzzy.simpleFilter = (pattern, array) -> 13 | array.filter (string) -> 14 | fuzzy.test pattern, string 15 | 16 | # Does `pattern` fuzzy match `string`? 17 | fuzzy.test = (pattern, string) -> 18 | fuzzy.match(pattern, string) isnt null 19 | 20 | # If `pattern` (input) matches `string` (test against), wrap each matching 21 | # character in `opts.pre` and `opts.post`. If no match, return null 22 | fuzzy.match = (pattern, string, opts={}) -> 23 | patternIdx = 0 24 | result = [] 25 | len = string.length 26 | totalScore = 0 27 | currScore = 0 28 | 29 | # prefix 30 | pre = opts.pre or "" 31 | 32 | # suffix 33 | post = opts.post or "" 34 | 35 | # String to compare against. This might be a lowercase version of the 36 | # raw string 37 | compareString = opts.caseSensitive and string or string.toLowerCase() 38 | ch = undefined 39 | compareChar = undefined 40 | pattern = opts.caseSensitive and pattern or pattern.toLowerCase() 41 | 42 | # For each character in the string, either add it to the result 43 | # or wrap in template if its the next string in the pattern 44 | idx = 0 45 | while idx < len 46 | # Ignore Whitespaces 47 | patternIdx++ if pattern[patternIdx] is ' ' 48 | 49 | ch = string[idx] 50 | if compareString[idx] is pattern[patternIdx] 51 | ch = pre + ch + post 52 | patternIdx += 1 53 | 54 | currScore += 1 + currScore 55 | else 56 | currScore = 0 57 | totalScore += currScore 58 | result[result.length] = ch 59 | idx++ 60 | return {rendered: result.join(""), score: totalScore} if patternIdx is pattern.length 61 | 62 | fuzzy.filter = (pattern, arr, opts={}) -> 63 | highlighted = arr.reduce( 64 | (prev, element, idx, arr) -> 65 | str = element 66 | str = opts.extract(element) if opts.extract 67 | rendered = fuzzy.match(pattern, str, opts) 68 | if rendered? 69 | prev[prev.length] = 70 | string: rendered.rendered 71 | score: rendered.score 72 | index: idx 73 | original: element 74 | prev 75 | ,[] 76 | ).sort (a, b) -> 77 | compare = b.score - a.score 78 | if compare is 0 79 | return opts.extract(a.original).length - opts.extract(b.original).length if opts.extract 80 | return a.original.length - b.original.length 81 | return compare if compare 82 | a.index - b.index 83 | 84 | # No matches? Sort the original array using Damerau-Levenshtein. 85 | if highlighted.length < 1 86 | highlighted = arr.reduce( 87 | (prev, element, idx, arr) -> 88 | str = element 89 | str = opts.extract(element) if opts.extract 90 | prev[prev.length] = 91 | string: str 92 | score: levenshtein(pattern, str) 93 | index: idx 94 | original: element 95 | prev 96 | ,[] 97 | ).sort (a, b) -> 98 | compare = a.score - b.score 99 | return compare if compare 100 | b.index - a.index 101 | highlighted 102 | 103 | ### 104 | # Copyright (c) 2011 Andrei Mackenzie 105 | # 106 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 107 | # this software and associated documentation files (the "Software"), to deal in 108 | # the Software without restriction, including without limitation the rights to 109 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 110 | # the Software, and to permit persons to whom the Software is furnished to do so, 111 | # subject to the following conditions: 112 | # 113 | # The above copyright notice and this permission notice shall be included in all 114 | # copies or substantial portions of the Software. 115 | # 116 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 117 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 118 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 119 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 120 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 121 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 122 | ### 123 | 124 | # Compute the edit distance between the two given strings 125 | levenshtein = (a, b) -> 126 | return b.length if a.length is 0 127 | return a.length if b.length is 0 128 | matrix = [] 129 | 130 | # increment along the first column of each row 131 | i = undefined 132 | i = 0 133 | while i <= b.length 134 | matrix[i] = [i] 135 | i++ 136 | 137 | # increment each column in the first row 138 | j = undefined 139 | j = 0 140 | while j <= a.length 141 | matrix[0][j] = j 142 | j++ 143 | 144 | # Fill in the rest of the matrix 145 | i = 1 146 | while i <= b.length 147 | j = 1 148 | while j <= a.length 149 | if b.charAt(i - 1) is a.charAt(j - 1) 150 | matrix[i][j] = matrix[i - 1][j - 1] 151 | else 152 | # substitution 153 | # insertion 154 | matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, Math.min(matrix[i][j - 1] + 1, matrix[i - 1][j] + 1)) # deletion 155 | j++ 156 | i++ 157 | matrix[b.length][a.length] 158 | -------------------------------------------------------------------------------- /lib/git-plus-commands.coffee: -------------------------------------------------------------------------------- 1 | git = require './git' 2 | 3 | getCommands = -> 4 | GitAdd = require './models/git-add' 5 | GitAddAllAndCommit = require './models/git-add-all-and-commit' 6 | GitAddAllCommitAndPush = require './models/git-add-all-commit-and-push' 7 | GitAddAndCommit = require './models/git-add-and-commit' 8 | GitBranch = require './models/git-branch' 9 | GitDeleteLocalBranch = require './models/git-delete-local-branch.coffee' 10 | GitDeleteRemoteBranch = require './models/git-delete-remote-branch.coffee' 11 | GitCheckoutAllFiles = require './models/git-checkout-all-files' 12 | GitCheckoutCurrentFile = require './models/git-checkout-current-file' 13 | GitCherryPick = require './models/git-cherry-pick' 14 | GitCommit = require './models/git-commit' 15 | GitCommitAmend = require './models/git-commit-amend' 16 | GitDiff = require './models/git-diff' 17 | GitDiffAll = require './models/git-diff-all' 18 | GitFetch = require './models/git-fetch' 19 | GitFetchPrune = require './models/git-fetch-prune.coffee' 20 | GitInit = require './models/git-init' 21 | GitLog = require './models/git-log' 22 | GitPull = require './models/git-pull' 23 | GitPush = require './models/git-push' 24 | GitRemove = require './models/git-remove' 25 | GitShow = require './models/git-show' 26 | GitStageFiles = require './models/git-stage-files' 27 | GitStageHunk = require './models/git-stage-hunk' 28 | GitStashApply = require './models/git-stash-apply' 29 | GitStashDrop = require './models/git-stash-drop' 30 | GitStashPop = require './models/git-stash-pop' 31 | GitStashSave = require './models/git-stash-save' 32 | GitStatus = require './models/git-status' 33 | GitTags = require './models/git-tags' 34 | GitUnstageFiles = require './models/git-unstage-files' 35 | GitRun = require './models/git-run' 36 | GitMerge = require './models/git-merge' 37 | 38 | git.getRepo() 39 | .then (repo) -> 40 | git.refresh() 41 | commands = [] 42 | commands.push ['git-plus:add', 'Add', -> GitAdd(repo)] 43 | commands.push ['git-plus:add-all', 'Add All', -> GitAdd(repo, addAll: true)] 44 | commands.push ['git-plus:log', 'Log', -> GitLog(repo)] 45 | commands.push ['git-plus:log-current-file', 'Log Current File', -> GitLog(repo, onlyCurrentFile: true)] 46 | commands.push ['git-plus:remove-current-file', 'Remove Current File', -> GitRemove(repo)] 47 | commands.push ['git-plus:checkout-all-files', 'Checkout All Files', -> GitCheckoutAllFiles(repo)] 48 | commands.push ['git-plus:checkout-current-file', 'Checkout Current File', -> GitCheckoutCurrentFile(repo)] 49 | commands.push ['git-plus:commit', 'Commit', -> new GitCommit(repo)] 50 | commands.push ['git-plus:commit-all', 'Commit All', -> new GitCommit(repo, stageChanges: true)] 51 | commands.push ['git-plus:commit-amend', 'Commit Amend', -> GitCommitAmend(repo)] 52 | commands.push ['git-plus:add-and-commit', 'Add And Commit', -> GitAddAndCommit(repo)] 53 | commands.push ['git-plus:add-all-and-commit', 'Add All And Commit', -> GitAddAllAndCommit(repo)] 54 | commands.push ['git-plus:add-all-commit-and-push', 'Add All Commit And Push', -> GitAddAllCommitAndPush(repo)] 55 | commands.push ['git-plus:checkout', 'Checkout', -> GitBranch.gitBranches(repo)] 56 | commands.push ['git-plus:checkout-remote', 'Checkout Remote', -> GitBranch.gitRemoteBranches(repo)] 57 | commands.push ['git-plus:new-branch', 'Checkout New Branch', -> GitBranch.newBranch(repo)] 58 | commands.push ['git-plus:delete-local-branch', 'Delete Local Branch', -> GitDeleteLocalBranch(repo)] 59 | commands.push ['git-plus:delete-remote-branch', 'Delete Remote Branch', -> GitDeleteRemoteBranch(repo)] 60 | commands.push ['git-plus:cherry-pick', 'Cherry-Pick', -> GitCherryPick(repo)] 61 | commands.push ['git-plus:diff', 'Diff', -> GitDiff(repo)] 62 | commands.push ['git-plus:diff-all', 'Diff All', -> GitDiffAll(repo)] 63 | commands.push ['git-plus:fetch', 'Fetch', -> GitFetch(repo)] 64 | commands.push ['git-plus:fetch-prune', 'Fetch Prune', -> GitFetchPrune(repo)] 65 | commands.push ['git-plus:pull', 'Pull', -> GitPull(repo)] 66 | commands.push ['git-plus:pull-using-rebase', 'Pull Using Rebase', -> GitPull(repo, rebase: true)] 67 | commands.push ['git-plus:push', 'Push', -> GitPush(repo)] 68 | commands.push ['git-plus:remove', 'Remove', -> GitRemove(repo, showSelector: true)] 69 | commands.push ['git-plus:reset', 'Reset HEAD', -> git.reset(repo)] 70 | commands.push ['git-plus:show', 'Show', -> GitShow(repo)] 71 | commands.push ['git-plus:stage-files', 'Stage Files', -> GitStageFiles(repo)] 72 | commands.push ['git-plus:unstage-files', 'Unstage Files', -> GitUnstageFiles(repo)] 73 | commands.push ['git-plus:stage-hunk', 'Stage Hunk', -> GitStageHunk(repo)] 74 | commands.push ['git-plus:stash-save-changes', 'Stash: Save Changes', -> GitStashSave(repo)] 75 | commands.push ['git-plus:stash-pop', 'Stash: Apply (Pop)', -> GitStashPop(repo)] 76 | commands.push ['git-plus:stash-apply', 'Stash: Apply (Keep)', -> GitStashApply(repo)] 77 | commands.push ['git-plus:stash-delete', 'Stash: Delete (Drop)', -> GitStashDrop(repo)] 78 | commands.push ['git-plus:status', 'Status', -> GitStatus(repo)] 79 | commands.push ['git-plus:tags', 'Tags', -> GitTags(repo)] 80 | commands.push ['git-plus:run', 'Run', -> new GitRun(repo)] 81 | commands.push ['git-plus:merge', 'Merge', -> GitMerge(repo)] 82 | 83 | return commands 84 | 85 | module.exports = getCommands 86 | -------------------------------------------------------------------------------- /lib/views/select-list-multiple-view.coffee: -------------------------------------------------------------------------------- 1 | fuzzyFilter = require('../models/fuzzy').filter 2 | {$, $$, View, SelectListView} = require 'atom-space-pen-views' 3 | 4 | # Public: Provides a view that renders a list of items with an editor that 5 | # filters the items. Enables you to select multiple items at once. 6 | # 7 | # Subclasses must implement the following methods: 8 | # 9 | # * {::viewForItem} 10 | # * {::completed} 11 | # 12 | # Subclasses should implement the following methods: 13 | # 14 | # * {::addButtons} 15 | # 16 | # ## Requiring in packages 17 | # 18 | # ```coffee 19 | # {SelectListMultipleView} = require 'atom' 20 | # 21 | # class MySelectListView extends SelectListMultipleView 22 | # initialize: -> 23 | # super 24 | # @addClass('overlay from-top') 25 | # @setItems(['Hello', 'World']) 26 | # atom.workspaceView.append(this) 27 | # @focusFilterEditor() 28 | # 29 | # viewForItem: (item) -> 30 | # "