branch_cli 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4b756cb09351d8216b599d29aa0d6ee7cccc79f2
4
+ data.tar.gz: 122054773404b0c5e5381c9c43b1d72df359e9b6
5
+ SHA512:
6
+ metadata.gz: 16968f525b1cc7177c2afe372e962462b26f5798e8e66bceec21110ddd2b4b97ebd7b3862cb6a16d01a3b5bef0eeeaaabab655dd86444dcea8227f10c1c587e0
7
+ data.tar.gz: a3b917ad7dbadf14a6840fa6c7ab9bbb476db48a6a4793161e4ad97b7b1ba95ec0d9c0b8bbb4a371d5a9c0d7dc8a62f479dd0a18ec919b9bb048344afa3f0a2b
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ spec/examples.txt
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,52 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ branch_cli (0.7.0)
5
+ binding_of_caller (~> 0.7.2)
6
+ formatador (~> 0.2.5)
7
+ inquirer (~> 0.2.1)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ binding_of_caller (0.7.2)
13
+ debug_inspector (>= 0.0.1)
14
+ coderay (1.1.1)
15
+ debug_inspector (0.0.3)
16
+ diff-lcs (1.3)
17
+ formatador (0.2.5)
18
+ inquirer (0.2.1)
19
+ term-ansicolor (>= 1.2.2)
20
+ method_source (0.8.2)
21
+ pry (0.10.4)
22
+ coderay (~> 1.1.0)
23
+ method_source (~> 0.8.1)
24
+ slop (~> 3.4)
25
+ rspec (3.6.0)
26
+ rspec-core (~> 3.6.0)
27
+ rspec-expectations (~> 3.6.0)
28
+ rspec-mocks (~> 3.6.0)
29
+ rspec-core (3.6.0)
30
+ rspec-support (~> 3.6.0)
31
+ rspec-expectations (3.6.0)
32
+ diff-lcs (>= 1.2.0, < 2.0)
33
+ rspec-support (~> 3.6.0)
34
+ rspec-mocks (3.6.0)
35
+ diff-lcs (>= 1.2.0, < 2.0)
36
+ rspec-support (~> 3.6.0)
37
+ rspec-support (3.6.0)
38
+ slop (3.6.0)
39
+ term-ansicolor (1.6.0)
40
+ tins (~> 1.0)
41
+ tins (1.14.0)
42
+
43
+ PLATFORMS
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ branch_cli!
48
+ pry (~> 0.10.4)
49
+ rspec (~> 3.6)
50
+
51
+ BUNDLED WITH
52
+ 1.14.6
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Daniel Inkpen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # branch cli
2
+
3
+ `branch` simplifies the average workflow of Git. By working on simple assumptions of workflow, it's easier, more memorable, quicker and safer to use.
4
+
5
+ The assumptions `branch` makes are:
6
+ - You don't care about staging/unstaging files. All changes are treated as staged.
7
+ - You use a single remote (origin) and your local "my-branch" is always going to have the upstream as "origin/my-branch"
8
+
9
+ ![Screenshot](screenshot.png)
10
+
11
+ ## Basic usage
12
+
13
+ - Use `branch` as an alternative for `git status`
14
+ - to change branch, use `branch BRANCH-NAME`
15
+ - To get a list of the most recent branches `branch --list`
16
+
17
+ ## Features
18
+
19
+ - When changing branch
20
+ - Warns if you have local changes and prompts on whether to continue
21
+ - Warns if the remote branch has diverged and prompts on whether you want to keep your local branch or to reset with the remote
22
+ - Formatted list of most recent branches with `--list` argument
23
+
24
+ ## Future ideas/plans
25
+
26
+ - Recording base branch when creating a new one
27
+ - Suggestion to rebase if it detects that you should (i.e. automatic `git pull --rebase` and `git rebase origin/base-branch`)
28
+ - Nicer flow for rebasing (visual representation of progress)
29
+ - clear indication of files in conflict (as well as automatic indication of when you have resolved them)
data/bin/branch ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'branch_cli'
4
+ main(ARGV)
@@ -0,0 +1,24 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = 'branch_cli'
6
+ gem.version = '0.7.0'
7
+ gem.authors = ['Daniel Inkpen']
8
+ gem.email = ['dan2552@gmail.com']
9
+ gem.description = %q{Faster, safer git branching.}
10
+ gem.summary = %q{Branch aims to simplify a developer's daily workflow of Git. It is in no means supposed to replace Git, but provide a quicker and easier way to do some more common functions (with more memorable commands). Branch is pretty opinionated in the way it does things (i.e. I don't care about staging/unstaging files, I just want all of my current changes in a single bucket). It also makes the assumption that you use a single remote (origin) and your local "my-branch" is always going to have the upstream as "origin/my-branch" (i.e. it fits with the most common of workflows).}
11
+ gem.homepage = 'https://github.com/Dan2552/branch'
12
+ gem.license = 'MIT'
13
+
14
+ gem.add_dependency "formatador", '~> 0.2.5'
15
+ gem.add_dependency "inquirer", '~> 0.2.1'
16
+ gem.add_dependency "binding_of_caller", '~> 0.7.2'
17
+
18
+ gem.add_development_dependency 'rspec', '~> 3.6'
19
+ gem.add_development_dependency 'pry', '~> 0.10.4'
20
+
21
+ gem.files = `git ls-files`.split($/)
22
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
23
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
24
+ end
data/lib/branch_cli.rb ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ module BranchCli
3
+ def self.root
4
+ File.dirname __dir__
5
+ end
6
+ end
7
+
8
+ require 'open3'
9
+
10
+ path = File.expand_path('../../lib/branch_cli', __FILE__)
11
+ $LOAD_PATH.unshift path
12
+
13
+ require 'formatador'
14
+ require 'inquirer'
15
+ require 'binding_of_caller'
16
+
17
+ require 'swift_compatibility'
18
+
19
+ require 'branch'
20
+ require 'commit'
21
+ require 'options'
22
+ require 'print'
23
+ require 'run'
24
+ require 'string'
25
+
26
+ begin
27
+ require 'main'
28
+ rescue Interrupt
29
+ exit 1
30
+ end
@@ -0,0 +1,196 @@
1
+ class Branch < SwiftStruct
2
+ let name = nil
3
+
4
+ def origin
5
+ return "origin/#{name}"
6
+ end
7
+ end
8
+
9
+ def getCurrentBranch
10
+ let result = runCommand("git symbolic-ref HEAD")
11
+ let matches = result.stdout.matches(forRegex: "heads\\/(.*)")
12
+ if matches.count == 0
13
+ return nil
14
+ else
15
+ return Branch.new(name: matches[0])
16
+ end
17
+ end
18
+
19
+ def setCurrentBranch(branch)
20
+ prettyPrint("Switching to branch #{branch.name.s.Bold}...")
21
+ fetch
22
+ detectChanges
23
+ resetLocal
24
+ switchBranch(branch)
25
+ detectAhead
26
+ resetToOrigin
27
+ end
28
+
29
+ def detectChanges
30
+ let status = gitStatus
31
+ if status.contains("to be committed") || status.contains("for commit:") || status.contains("Untracked files:")
32
+ uncommitedChanges
33
+ end
34
+ end
35
+
36
+ def resetLocal
37
+ runCommand("git reset --hard")
38
+ end
39
+
40
+ def switchBranch(branch)
41
+ runCommand("git checkout #{branch.name}")
42
+
43
+ if getCurrentBranch.name != branch.name
44
+ runCommand("git checkout -b #{branch.name}")
45
+ end
46
+
47
+ runCommand("git branch --set-upstream-to=origin/#{branch.name}")
48
+
49
+ if getCurrentBranch.name != branch.name
50
+ prettyPrint("🤔 Failed to switch branch".f.Red)
51
+ exit(1)
52
+ end
53
+ end
54
+
55
+ def detectAhead
56
+ let status = gitStatus
57
+ if status.contains("can be fast-forwarded.") || status.contains("is ahead of 'origin") || status.contains(" have diverged")
58
+ branchesDiverged
59
+ end
60
+ end
61
+
62
+ # On branch master
63
+ # Your branch and 'origin/master' have diverged,
64
+ # and have 1 and 1 different commit each, respectively.
65
+ # (use "git pull" to merge the remote branch into yours)
66
+ # nothing to commit, working directory clean
67
+ #
68
+ # On branch master
69
+ # Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
70
+ # (use "git pull" to update your local branch)
71
+ # nothing to commit, working directory clean
72
+ #
73
+ # On branch master
74
+ # Your branch is ahead of 'origin/master' by 1 commit.
75
+ # (use "git push" to publish your local commits)
76
+ # nothing to commit, working directory clean
77
+ def branchesDiverged
78
+ prettyPrint("\n😱 You appear to have a diverged branch:".f.Red)
79
+ let matches = gitStatus.matches(forRegex: "(Your branch .*\\s*.*)")
80
+
81
+ matches.each do |match|
82
+ prettyPrint(match)
83
+ end
84
+
85
+ promptKeepLocal
86
+ end
87
+
88
+ def resetToOrigin
89
+ let origin = getCurrentBranch.origin
90
+ let reset = runCommand("git reset --hard #{origin}")
91
+
92
+ if reset.exitStatus == 0
93
+ prettyPrint("Using remote branch".f.Green)
94
+ else
95
+ prettyPrint("Using local branch (no origin branch found)".f.Green)
96
+ end
97
+ end
98
+
99
+ def printCurrentBranch
100
+ let branchName = getCurrentBranch&.name || "no branch"
101
+ prettyPrint("On branch #{branchName.s.Bold}")
102
+ end
103
+
104
+ def printRecentBranches
105
+ let command = runCommand("git for-each-ref --sort=-committerdate --format=\"%(refname)\" --count=30 refs/heads/ refs/remotes")
106
+ let references = command.stdout.components(separatedBy: "\n")
107
+
108
+ commits = references.map do |r|
109
+ Commit.new(message: "", sha: r.clearQuotes)
110
+ end
111
+
112
+ strings = commits.map do |c|
113
+ c.printableFormat("%Cgreen%cr%Creset %C(yellow)%d%Creset %C(bold blue)<%an>%Creset%n")
114
+ end
115
+ var lastStr = ""
116
+ strings.each do |str|
117
+ if str != lastStr
118
+ prettyPrint(str) # avoid dups
119
+ end
120
+ lastStr = str
121
+ end
122
+ end
123
+
124
+ def uncommitedChanges
125
+ prettyPrint("\n😱 You appear to have uncommited changes:".f.Red)
126
+ printGitStatus
127
+ promptContinueAnyway
128
+ end
129
+
130
+ def gitStatus
131
+ return runCommand("git status").stdout
132
+ end
133
+
134
+ def printGitStatus(preceedingNewline: false)
135
+ let diff = gitStatus.matches(forRegex: "\t([a-z ]*:.*)")
136
+ if diff.count > 0 && preceedingNewline
137
+ prettyPrint("")
138
+ end
139
+
140
+ diff.each do |line|
141
+ prettyPrint("\t#{line}".f.Green)
142
+ end
143
+ end
144
+
145
+ def promptContinueAnyway
146
+ puts ""
147
+ response = Ask.list "Continue anyway? Changes will be lost", [
148
+ "Stop",
149
+ "Continue"
150
+ ]
151
+
152
+ exit(1) if response == 0
153
+ end
154
+
155
+ def promptKeepLocal
156
+ let choice: String
157
+ let options = Options.sharedInstance
158
+
159
+ if options.preferLocal
160
+ choice = "local"
161
+ elsif options.preferRemote
162
+ choice = "remote"
163
+ else
164
+ puts ""
165
+ response = Ask.list "Keep remote or local copy?", [
166
+ "Remote",
167
+ "Local"
168
+ ]
169
+
170
+ choice = "remote" if response == 0
171
+ choice = "local" if response == 1
172
+ end
173
+
174
+ if choice != "remote"
175
+ prettyPrint("Using local branch (user specified)")
176
+ exit(0)
177
+ end
178
+ end
179
+
180
+ def addAll
181
+ runCommand("git reset --mixed")
182
+ runCommand("git add . -A")
183
+ end
184
+
185
+ def fetch
186
+ let fetch = runCommand("git fetch")
187
+ let error = fetch.stderr
188
+ if error.contains("No remote repository specified")
189
+ prettyPrint("\n⚠️ No remote repository is setup\n".f.Yellow)
190
+ return
191
+ end
192
+ if fetch.exitStatus != 0
193
+ prettyPrint("\n🤔 Failed to fetch.".f.Red)
194
+ exit(1)
195
+ end
196
+ end
@@ -0,0 +1,55 @@
1
+ class Commit < SwiftStruct
2
+ var message = nil
3
+ var sha = nil
4
+
5
+ def initialize(message: String, sha: String)
6
+ self.message = message.clearQuotes
7
+ self.sha = sha.clearQuotes
8
+ end
9
+
10
+ def self.getCurrentHead
11
+ return Commit.from(identifier: "HEAD")
12
+ end
13
+
14
+ def self.from(identifier: String)
15
+ return Commit.new(
16
+ message: message(forIdentifier: identifier),
17
+ sha: sha(forIdentifier: identifier)
18
+ )
19
+ end
20
+
21
+ def commitsLeading(commit: Commit)
22
+ let run = runCommand("git rev-list #{sha}..#{commit.sha} --reverse").stdout
23
+ var commits = [] # [Commit]
24
+ let shas = run.components(separatedBy: "\n")
25
+ shas.each do |sha|
26
+ commits.append(Commit.from(identifier: sha))
27
+ end
28
+ return commits
29
+ end
30
+
31
+ def mostRecentCommonAncestor(commit: Commit)
32
+ let mergeBase = runCommand("git merge-base #{sha} #{commit.sha}").stdout
33
+ return Commit.from(identifier: mergeBase)
34
+ end
35
+
36
+ def printableFormat(format)
37
+ let command = runCommand("git", args: [
38
+ "log",
39
+ "-n1",
40
+ sha,
41
+ "--format=\"#{format}\""
42
+ ])
43
+ return command.stdout.gsub("\n", "")
44
+ end
45
+
46
+ private
47
+
48
+ def self.sha(identifier: String)
49
+ return runCommand("git log -1 #{identifier} --format=\"%H\"").stdout
50
+ end
51
+
52
+ def self.message(identifier: String)
53
+ return runCommand("git log -1 #{identifier} --format=\"%s\"").stdout
54
+ end
55
+ end
@@ -0,0 +1,36 @@
1
+ def main(arguments)
2
+ Options.reset
3
+ let options = Options.sharedInstance
4
+ options.loadOptions(arguments: arguments)
5
+
6
+ if options.isShowVersion
7
+ spec = Gem::Specification::load("#{BranchCli.root}/branch_cli.gemspec")
8
+ prettyPrint("branch cli #{spec.version}")
9
+ exit(0)
10
+ end
11
+
12
+ if options.isShowList
13
+ printRecentBranches
14
+ exit(0)
15
+ end
16
+
17
+ if options.isHelp
18
+ prettyPrint("usage: branch BRANCH-NAME [ARGS]")
19
+ prettyPrint("")
20
+ prettyPrint("--version | -v \tShows the current version")
21
+ prettyPrint("--verbose \t\tPrints all the git commands as they run")
22
+ prettyPrint("--list | -l \t\tPrints the most recently updated branches")
23
+ prettyPrint("--prefer=PREFERENCE \tWhere PREFERENCE is local or remote, will use the set preference rather than ask")
24
+ prettyPrint("--help | help \tShows this help")
25
+ exit(0)
26
+ end
27
+
28
+ addAll
29
+
30
+ if options.isBranchSupplied
31
+ setCurrentBranch(Branch.new(name: options.suppliedBranch))
32
+ else
33
+ printCurrentBranch
34
+ printGitStatus(preceedingNewline: true)
35
+ end
36
+ end
@@ -0,0 +1,58 @@
1
+ class Options < SwiftObject
2
+ def self.sharedInstance
3
+ @sharedInstance ||= Options.new
4
+ end
5
+
6
+ def self.reset
7
+ @sharedInstance = nil
8
+ end
9
+
10
+ var isVerbose = false
11
+ var isShowVersion = false
12
+ var suppliedBranch = nil
13
+ var isHelp = false
14
+ var isTestRebase = false
15
+ var isShowList = false
16
+ var preferLocal = false
17
+ var preferRemote = false
18
+
19
+ def isBranchSupplied
20
+ return suppliedBranch != nil
21
+ end
22
+
23
+ def loadOptions(arguments:)
24
+ if arguments.contains("help") || arguments.contains("--help")
25
+ self.isHelp = true
26
+ end
27
+
28
+ if arguments.contains("--verbose")
29
+ self.isVerbose = true
30
+ end
31
+
32
+ if arguments.contains("-v") || arguments.contains("--version")
33
+ self.isShowVersion = true
34
+ end
35
+
36
+ if arguments.contains("--test-rebase")
37
+ self.isTestRebase = true
38
+ end
39
+
40
+ if arguments.contains("--list") || arguments.contains("-l")
41
+ self.isShowList = true
42
+ end
43
+
44
+ if arguments.contains("--prefer=local")
45
+ self.preferLocal = true
46
+ end
47
+
48
+ if arguments.contains("--prefer=remote")
49
+ self.preferRemote = true
50
+ end
51
+
52
+ if arguments.count > 0
53
+ if !arguments[0].hasPrefix("-")
54
+ self.suppliedBranch = arguments[0]
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,5 @@
1
+ def prettyPrint(str)
2
+ printer = Formatador.new
3
+ printer.instance_eval { @indent = 0 }
4
+ printer.display_line(str.strip)
5
+ end
@@ -0,0 +1,19 @@
1
+ class RunResult < SwiftStruct
2
+ let stdout = nil
3
+ let stderr = nil
4
+ let exitStatus = nil
5
+ end
6
+
7
+ def runCommand(command, args: [])
8
+ command_with_args = "#{command} #{args.join(" ")}"
9
+
10
+ if Options.sharedInstance.isVerbose
11
+ prettyPrint(command_with_args.f.Blue)
12
+ end
13
+
14
+ Open3.popen3(command_with_args) do |stdin, stdout, stderr, wait_thr|
15
+ RunResult.new(stdout: stdout.read,
16
+ stderr: stderr.read,
17
+ exitStatus: wait_thr.value.to_i)
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ class String
2
+ def matches(forRegex:)
3
+ lines = split("\n")
4
+ lines.map { |l| l.match(forRegex)&.captures }.flatten.compact
5
+ end
6
+
7
+ def clearQuotes
8
+ gsub("\"", with: "")
9
+ end
10
+
11
+ def s
12
+ self
13
+ end
14
+
15
+ def Bold
16
+ "[bold]#{self}[/]"
17
+ end
18
+
19
+ def f
20
+ self
21
+ end
22
+
23
+ def Green
24
+ "[green]#{self}[/]"
25
+ end
26
+
27
+ def Blue
28
+ "[blue]#{self}[/]"
29
+ end
30
+
31
+ def Red
32
+ "[red]#{self}[/]"
33
+ end
34
+
35
+ def Yellow
36
+ "[yellow]#{self}[/]"
37
+ end
38
+ end
@@ -0,0 +1,70 @@
1
+ class Object
2
+ def let(*args)
3
+ end
4
+
5
+ def var(*args)
6
+ end
7
+ end
8
+
9
+ class SwiftObject
10
+ def self.let(*args)
11
+ bind = binding.of_caller(1)
12
+ bind.local_variables.each do |v|
13
+ attr_reader(v)
14
+
15
+ variable_defaults[v] = bind.local_variable_get(v)
16
+ end
17
+ end
18
+
19
+ def self.var(*args)
20
+ bind = binding.of_caller(1)
21
+ bind.local_variables.each do |v|
22
+ attr_reader(v)
23
+ attr_writer(v)
24
+
25
+ variable_defaults[v] = bind.local_variable_get(v)
26
+ end
27
+ end
28
+
29
+ def self.variable_defaults
30
+ @variable_defaults ||= {}
31
+ end
32
+
33
+ def initialize
34
+ params = self.class.variable_defaults
35
+
36
+ params.each do |key, value|
37
+ instance_variable_set("@#{key}", value)
38
+ end
39
+ end
40
+ end
41
+
42
+ class SwiftStruct < SwiftObject
43
+ def initialize(params = {})
44
+ params = self.class.variable_defaults.merge!(params)
45
+
46
+ params.each do |key, value|
47
+ instance_variable_set("@#{key}", value)
48
+ end
49
+ end
50
+ end
51
+
52
+ class Array
53
+ def contains(*args)
54
+ include?(*args)
55
+ end
56
+ end
57
+
58
+ class String
59
+ def contains(*args)
60
+ include?(*args)
61
+ end
62
+
63
+ def hasPrefix(str)
64
+ start_with?(str)
65
+ end
66
+
67
+ def components(separatedBy:)
68
+ split(separatedBy)
69
+ end
70
+ end
data/screenshot.png ADDED
Binary file
@@ -0,0 +1,8 @@
1
+ RSpec.describe "Help" do
2
+ subject { execute(args) }
3
+ let(:args) { %w{--help} }
4
+
5
+ it "prints out help text" do
6
+ expect_output /Where PREFERENCE is local or remote/
7
+ end
8
+ end
@@ -0,0 +1,29 @@
1
+ RSpec.describe "status" do
2
+ subject { execute(args) }
3
+ let(:args) { [] }
4
+
5
+ it "prints the current branch" do
6
+ expect_output /On branch .*master/
7
+ end
8
+
9
+ context "when there are changed files" do
10
+ before do
11
+ touch "awholenewfile"
12
+ end
13
+
14
+ it "prints out the files" do
15
+ expect_output /awholenewfile/
16
+ end
17
+ end
18
+
19
+ context "when there are committed files" do
20
+ before do
21
+ touch "awholenewfile"
22
+ commit
23
+ end
24
+
25
+ it "does not print committed files" do
26
+ expect_to_not_output "awholenewfile"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,57 @@
1
+ RSpec.describe "switching branch" do
2
+ subject { execute(args) }
3
+ let(:args) { %w{new-branch} }
4
+
5
+ it "prints that it's switching branch" do
6
+ expect_output /Switching to branch .*new-branch/
7
+ end
8
+
9
+ it "switches the branch" do
10
+ subject
11
+ expect_branch "new-branch"
12
+ end
13
+
14
+ context "when the branch doesn't exist on remote" do
15
+ it "prints that it's using local branch" do
16
+ expect_output /Using local branch \(no origin branch found\)/
17
+ end
18
+ end
19
+
20
+ context "when the working copy has changes" do
21
+ before { touch("changes") }
22
+
23
+ it "asks whether to discard changes" do
24
+ expect(Ask).to receive(:list)
25
+ .with("Continue anyway? Changes will be lost", ["Stop", "Continue"])
26
+ subject
27
+ end
28
+ end
29
+
30
+ context "when the branch exists on remote" do
31
+ let(:args) { %w{spec} }
32
+ before { clone_remote_repo }
33
+
34
+ context "when the local branch matches the remote" do
35
+ let(:args) { %w{master} }
36
+ before { execute %w{spec --prefer=remote} }
37
+
38
+ it "prints that it's using the remote branch" do
39
+ expect_output /Using remote branch/
40
+ end
41
+ end
42
+
43
+ context "when the local branch is diverged from the remote" do
44
+ before do
45
+ touch "changes"
46
+ commit
47
+ end
48
+ let(:args) { %w{master} }
49
+
50
+ it "asks whether to use the remote or local" do
51
+ expect(Ask).to receive(:list)
52
+ .with("Keep remote or local copy?", ["Remote", "Local"])
53
+ subject
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,9 @@
1
+ RSpec.describe "Version" do
2
+ subject { execute(args) }
3
+ let(:args) { %w{--version} }
4
+
5
+ it "prints out the version number of the release" do
6
+ spec = Gem::Specification::load("#{BranchCli.root}/branch_cli.gemspec")
7
+ expect_output /branch cli #{spec.version}/
8
+ end
9
+ end
@@ -0,0 +1,77 @@
1
+ require 'rubygems'
2
+ require 'pry'
3
+ require 'branch_cli'
4
+ require_relative 'support/cli'
5
+
6
+ RSpec.configure do |config|
7
+ # rspec-expectations config goes here. You can use an alternate
8
+ # assertion/expectation library such as wrong or the stdlib/minitest
9
+ # assertions if you prefer.
10
+ config.expect_with :rspec do |expectations|
11
+ # This option will default to `true` in RSpec 4. It makes the `description`
12
+ # and `failure_message` of custom matchers include text for helper methods
13
+ # defined using `chain`, e.g.:
14
+ # be_bigger_than(2).and_smaller_than(4).description
15
+ # # => "be bigger than 2 and smaller than 4"
16
+ # ...rather than:
17
+ # # => "be bigger than 2"
18
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
19
+ end
20
+
21
+ # rspec-mocks config goes here. You can use an alternate test double
22
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
23
+ config.mock_with :rspec do |mocks|
24
+ # Prevents you from mocking or stubbing a method that does not exist on
25
+ # a real object. This is generally recommended, and will default to
26
+ # `true` in RSpec 4.
27
+ mocks.verify_partial_doubles = true
28
+
29
+ # To allow expectations on `nil` and suppress this message, set `config.allow_message_expectations_on_nil` to `true`.
30
+ # To disallow expectations on `nil`, set `config.allow_message_expectations_on_nil` to `false`.
31
+ mocks.allow_message_expectations_on_nil = true
32
+ end
33
+
34
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
35
+ # have no way to turn it off -- the option exists only for backwards
36
+ # compatibility in RSpec 3). It causes shared context metadata to be
37
+ # inherited by the metadata hash of host groups and examples, rather than
38
+ # triggering implicit auto-inclusion in groups with matching metadata.
39
+ config.shared_context_metadata_behavior = :apply_to_host_groups
40
+
41
+ # This allows you to limit a spec run to individual examples or groups
42
+ # you care about by tagging them with `:focus` metadata. When nothing
43
+ # is tagged with `:focus`, all examples get run. RSpec also provides
44
+ # aliases for `it`, `describe`, and `context` that include `:focus`
45
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
46
+ config.filter_run_when_matching :focus
47
+
48
+ # Allows RSpec to persist some state between runs in order to support
49
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
50
+ # you configure your source control system to ignore this file.
51
+ config.example_status_persistence_file_path = "spec/examples.txt"
52
+
53
+ # Limits the available syntax to the non-monkey patched syntax that is
54
+ # recommended. For more details, see:
55
+ # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
56
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
57
+ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
58
+ config.disable_monkey_patching!
59
+
60
+ # Many RSpec users commonly either run the entire suite or an individual
61
+ # file, and it's useful to allow more verbose output when running an
62
+ # individual spec file.
63
+ if config.files_to_run.one?
64
+ config.default_formatter = 'doc'
65
+ end
66
+
67
+ # Print the 10 slowest examples and example groups at the
68
+ # end of the spec run, to help surface which specs are running
69
+ # particularly slow.
70
+ config.profile_examples = 10
71
+
72
+ # Run specs in random order to surface order dependencies. If you find an
73
+ # order dependency and want to debug it, you can fix the order by providing
74
+ # the seed, which is printed after each run.
75
+ # --seed 1234
76
+ config.order = :random
77
+ end
@@ -0,0 +1,46 @@
1
+ def execute(args)
2
+ @original = Dir.pwd
3
+ Dir.chdir "/tmp/branch-cli-test"
4
+ main(args)
5
+ rescue SystemExit
6
+ end
7
+
8
+ def create_test_repo
9
+ `mkdir /tmp/branch-cli-test`
10
+ `cd /tmp/branch-cli-test && git init`
11
+ end
12
+
13
+ def teardown_test_repo
14
+ `rm -rf /tmp/branch-cli-test`
15
+ Dir.chdir "/tmp"
16
+ end
17
+
18
+ def clone_remote_repo
19
+ teardown_test_repo
20
+ `cd /tmp && git clone https://github.com/Dan2552/branch.git branch-cli-test`
21
+ end
22
+
23
+ RSpec.configure do |config|
24
+ config.before(:each) { teardown_test_repo; create_test_repo }
25
+ config.after(:each) { teardown_test_repo }
26
+ end
27
+
28
+ def expect_output(out)
29
+ expect { subject }.to output(out).to_stdout
30
+ end
31
+
32
+ def expect_to_not_output(out)
33
+ expect { subject }.to_not output(out).to_stdout
34
+ end
35
+
36
+ def expect_branch(branch)
37
+ expect(`cd /tmp/branch-cli-test && git status`).to match(/On branch #{branch}/)
38
+ end
39
+
40
+ def touch(filename)
41
+ `cd /tmp/branch-cli-test && touch #{filename}`
42
+ end
43
+
44
+ def commit
45
+ `cd /tmp/branch-cli-test && git add . -A && git commit -m "test"`
46
+ end
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: branch_cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.0
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Inkpen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-05-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: formatador
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.2.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.2.5
27
+ - !ruby/object:Gem::Dependency
28
+ name: inquirer
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.2.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: binding_of_caller
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.7.2
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.7.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.6'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.10.4
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.10.4
83
+ description: Faster, safer git branching.
84
+ email:
85
+ - dan2552@gmail.com
86
+ executables:
87
+ - branch
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - Gemfile
94
+ - Gemfile.lock
95
+ - LICENSE
96
+ - README.md
97
+ - bin/branch
98
+ - branch_cli.gemspec
99
+ - lib/branch_cli.rb
100
+ - lib/branch_cli/branch.rb
101
+ - lib/branch_cli/commit.rb
102
+ - lib/branch_cli/main.rb
103
+ - lib/branch_cli/options.rb
104
+ - lib/branch_cli/print.rb
105
+ - lib/branch_cli/run.rb
106
+ - lib/branch_cli/string.rb
107
+ - lib/branch_cli/swift_compatibility.rb
108
+ - screenshot.png
109
+ - spec/integration/help_spec.rb
110
+ - spec/integration/status_spec.rb
111
+ - spec/integration/switch_branch_spec.rb
112
+ - spec/integration/version_spec.rb
113
+ - spec/spec_helper.rb
114
+ - spec/support/cli.rb
115
+ homepage: https://github.com/Dan2552/branch
116
+ licenses:
117
+ - MIT
118
+ metadata: {}
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ requirements: []
134
+ rubyforge_project:
135
+ rubygems_version: 2.6.8
136
+ signing_key:
137
+ specification_version: 4
138
+ summary: Branch aims to simplify a developer's daily workflow of Git. It is in no
139
+ means supposed to replace Git, but provide a quicker and easier way to do some more
140
+ common functions (with more memorable commands). Branch is pretty opinionated in
141
+ the way it does things (i.e. I don't care about staging/unstaging files, I just
142
+ want all of my current changes in a single bucket). It also makes the assumption
143
+ that you use a single remote (origin) and your local "my-branch" is always going
144
+ to have the upstream as "origin/my-branch" (i.e. it fits with the most common of
145
+ workflows).
146
+ test_files:
147
+ - spec/integration/help_spec.rb
148
+ - spec/integration/status_spec.rb
149
+ - spec/integration/switch_branch_spec.rb
150
+ - spec/integration/version_spec.rb
151
+ - spec/spec_helper.rb
152
+ - spec/support/cli.rb