git-utils 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2faeb0fba87e36f531addf4afed6971f1edf3aca
4
+ data.tar.gz: c74f6c8870f6f05b34976ebf29ce7803afebf1f4
5
+ SHA512:
6
+ metadata.gz: 2bb21163ca18e41b1695b00da352c8d259cd1f3b0e2db93feed285021ef44607c2b63f25063643cd601708356dbd140f651a92cbe9c4ad26da807c6acfe2541b
7
+ data.tar.gz: c0efe25871d671007427bf3cb2d28427f16a03d59b7086d768b046b2d513e1f7b0358a7be08d9847f8135b2927bcf22872f48f4a7597088701260e615fd18cb9
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rvmrc
19
+ .DS_Store
20
+ .api_token
21
+ .project_id
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1 @@
1
+ pivotal-github
@@ -0,0 +1 @@
1
+ ruby-2.0.0
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+ ruby '2.0.0'
3
+
4
+ gem 'rspec', '~> 2.13.0'
5
+
6
+ gemspec
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2013 Michael Hartl
2
+ Copyright (c) 2013 Aleksandar Simic
3
+
4
+ MIT License
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,85 @@
1
+ # Git utilities
2
+
3
+ This repo contains some Git utility scripts. The highlights are `git open`, `git pull-request`, `git push-branch`, and `git undo`, which you'll never understand how you did without.
4
+
5
+ The commands are especially useful when combined with [`pivotal-github`](https://github.com/mhartl/git-utils) gem (which, despite its name, also works with Bitbucket).
6
+
7
+ The `git-utils` used to be pure Bash scripts, but they are now available as a Ruby gem, both because Ruby is more powerful than bash and because now `git-utils` can be included more easily as a dependency for the [`pivotal-github`](https://github.com/mhartl/pivotal-github/) gem. As a result, installation is easy if you have RubyGems installed:
8
+
9
+ gem install git-utils
10
+
11
+ ## Commands
12
+
13
+ * `git amend`: alias for `git commit --amend`
14
+ * `git anal` (use with caution): makes a commit with the message "Make anal changes"
15
+ * `git cleanup`: deletes every branch already merged into current branch (apart from `master`, `staging`, and `development`)
16
+ * `git merge-branch [branch]`: merges current branch into given branch (defaults to `master`)
17
+ * `git open`: opens the remote page for the repo (OS X only)
18
+ * `git polish`: makes a commit with the message "Polish"
19
+ * `git pull-request`: opens the remote page for issuing a new a pull request (OS X only)
20
+ * `git push-branch`: pushes the current branch up to origin
21
+ * `git delete-remote-branch <branch>`: deletes the remote branch if it is safe to do so
22
+ * `git switch <pattern>`: switches to the first branch matching the given pattern
23
+ * `git sync`: syncs the local master with remote
24
+ * `git undo`: undoes the last commit
25
+
26
+ ## Aliases
27
+
28
+ Here are some suggested aliases:
29
+
30
+ git config --global alias.mb merge-branch
31
+ git config --global alias.pr pull-request
32
+ git config --global alias.pb push-branch
33
+
34
+ ## Further details
35
+
36
+ Some of these commands deserve further explanation.
37
+
38
+ ### git merge-branch
39
+
40
+ `git merge-branch [target]` merges the current branch into the target branch (defaults to `master`). On a branch called `add-markdown-support`, `git merge-branch` is equivalent to the following:
41
+
42
+ $ git checkout master
43
+ $ git merge --no-ff --log add-markdown-support
44
+
45
+ Note that this effectively changes the default merge behavior from fast-forward to no-fast-forward, which makes it possible to use `git log` to see which of the commit objects together have implemented a feature on a particular branch. As noted in [A successful Git branching model](http://nvie.com/posts/a-successful-git-branching-model/),
46
+
47
+ > The `--no-ff` flag causes the merge to always create a new commit object, even if the merge could be performed with a fast-forward. This avoids losing information about the historical existence of a feature branch and groups together all commits that together added the feature… Yes, it will create a few more (empty) commit objects, but the gain is much bigger than that cost.
48
+
49
+ In addition, the `--log` option puts the commit messages from the individual commits in the merge message, which is especially useful for viewing the full diff represented by the commit.
50
+
51
+ These options can be overriden (and thus restored to their defaults) by passing the options `-ff` or `--no-log`. `git merge-branch` accepts any options valid for `git merge`.
52
+
53
+ ### git push-branch
54
+
55
+ `git push-branch` creates a remote branch at `origin` with the name of the current branch:
56
+
57
+ $ git branch-push
58
+ * [new branch] add-markdown-support -> add-markdown-support
59
+
60
+ `git push-branch` accepts any options valid for `git push`.
61
+
62
+
63
+ ### git sync
64
+
65
+ `git sync` syncs the local `master` with the remote `master`. On a branch called `add-markdown-support`, `git sync` is equivalent to the following:
66
+
67
+ $ git checkout master
68
+ $ git pull
69
+ $ git checkout add-markdown-support
70
+
71
+ The purpose of `git sync` is to prepare the current branch for rebasing against `master`:
72
+
73
+ $ git sync
74
+ $ git rebase master
75
+
76
+ (This is essentially equivalent to
77
+
78
+ $ git fetch
79
+ $ git rebase origin/master
80
+
81
+ but I don't like having `master` and `origin/master` be different since that means you have to remember to run `git pull` on `master` some time down the line.)
82
+
83
+ ## Installation
84
+
85
+ gem install git-utils
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Opens the last commit message for editing.
4
+ system 'git commit --amend'
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Makes anal changes.
4
+ system 'git commit -am "Make anal changes"'
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Deletes (almost) every branch already merged into current branch.
4
+ # Exceptions are `master`, `staging`, and `development`, and the current
5
+ # branch, which are preserved.
6
+ preserved = "(master|staging|development)"
7
+ cmd = %(git branch --merged | grep -v "\*" | egrep -v "#{preserved}" | )
8
+ cmd += 'xargs -n 1 git branch -d'
9
+ system cmd
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+ require 'git-utils/delete_remote_branch'
4
+
5
+ # Deletes the remote branch on origin if it is safe to do so.
6
+ exit Command.run!(DeleteRemoteBranch, ARGV.dup)
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+ require 'git-utils/merge_branch'
4
+
5
+ # Merges the current branch into the given branch (defaults to master).
6
+ # E.g., 'git merge-branch foobar' merges the current branch into foobar.
7
+ # 'git merge-branch', merges the current branch into master.
8
+ # git merge-branch uses the --no-ff --log options to ensure that the
9
+ # merge creates a new commit object and that the individual commits appear
10
+ # in the log file.
11
+ exit Command.run!(MergeBranch, ARGV.dup)
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+ require 'git-utils/open'
4
+
5
+ # Opens the remote page for the repo (OS X only).
6
+ exit Command.run!(Open, ARGV.dup)
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Makes a commit with the message 'Polish'
4
+ system 'git commit -am "Polish"'
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+ require 'git-utils/pull_request'
4
+
5
+ # Opens the remote page for issuing a new pull request.
6
+ exit Command.run!(PullRequest, ARGV.dup)
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+ require 'git-utils/push_branch'
4
+
5
+ # Pushes the current branch to origin.
6
+ exit Command.run!(PushBranch, ARGV.dup)
7
+
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+ require 'git-utils/switch'
4
+
5
+ # Switches to the first branch matching the given pattern.
6
+ # E.g., 'git switch foobar' switches to 'the-foobar-branch'.
7
+ exit Command.run!(Switch, ARGV.dup)
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+ require 'git-utils/sync'
4
+
5
+ # Syncs the local master branch with remote.
6
+ exit Command.run!(Sync, ARGV.dup)
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Undoes the last commit and places the changes back in the staging area.
4
+ system 'git reset --soft HEAD^''
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'git-utils/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "git-utils"
8
+ gem.version = Git::Utils::VERSION
9
+ gem.authors = ["Michael Hartl"]
10
+ gem.email = ["michael@michaelhartl.com"]
11
+ gem.description = %q{Add some Git utilities}
12
+ gem.summary = %q{See the README for full documentation}
13
+ gem.homepage = "https://github.com/mhartl/git-utils"
14
+ gem.license = "MIT"
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+ end
@@ -0,0 +1,10 @@
1
+ require "git-utils/version"
2
+ require "git-utils/options"
3
+ require "git-utils/command"
4
+ require "git-utils/merge_branch"
5
+ require "git-utils/open"
6
+ require "git-utils/delete_remote_branch"
7
+ require "git-utils/push_branch"
8
+ require "git-utils/switch"
9
+ require "git-utils/sync"
10
+ require "git-utils/pull_request"
@@ -0,0 +1,100 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+ require 'git-utils/options'
4
+
5
+ class Command
6
+ attr_accessor :args, :cmd, :options, :known_options, :unknown_options
7
+
8
+ def initialize(args = [])
9
+ self.args = args
10
+ self.options = OpenStruct.new
11
+ parse
12
+ end
13
+
14
+ def parse
15
+ self.known_options = Options::known_options(parser, args)
16
+ self.unknown_options = Options::unknown_options(parser, args)
17
+ parser.parse!(known_options)
18
+ end
19
+
20
+ def parser
21
+ OptionParser.new
22
+ end
23
+
24
+ # Returns the current Git branch.
25
+ def current_branch
26
+ @current_branch ||= `git rev-parse --abbrev-ref HEAD`.strip
27
+ end
28
+
29
+ # Returns the URL for the remote origin.
30
+ def origin_url
31
+ @origin_url ||= `git config --get remote.origin.url`.strip
32
+ end
33
+
34
+
35
+
36
+ # Returns the name of the repository service.
37
+ # It's currently GitHub, Bitbucket, or Stash.
38
+ # We return blank for an unknown service; the command will still
39
+ # often work in that case.
40
+ def service
41
+ if origin_url =~ /github/i
42
+ 'github'
43
+ elsif origin_url =~ /bitbucket/i
44
+ 'bitbucket'
45
+ elsif origin_url =~ /stash/i
46
+ 'stash'
47
+ else
48
+ ''
49
+ end
50
+ end
51
+
52
+ # Returns the protocol of the origin URL (defaults to ssh).
53
+ def protocol
54
+ if origin_url =~ /https?:\/\//
55
+ 'http'
56
+ else
57
+ 'ssh'
58
+ end
59
+ end
60
+
61
+ # Runs a command.
62
+ # If the argument array contains '--debug', returns the command that would
63
+ # have been run.
64
+ def self.run!(command_class, args)
65
+ debug = args.delete('--debug')
66
+ command = command_class.new(args)
67
+ if debug
68
+ puts command.cmd
69
+ return 1
70
+ else
71
+ command.run!
72
+ return 0
73
+ end
74
+ end
75
+
76
+ def run!
77
+ system cmd
78
+ end
79
+
80
+ private
81
+
82
+ # Returns an argument string based on given arguments.
83
+ # The main trick is to add in quotes for option
84
+ # arguments when necessary.
85
+ # For example, ['-a', '-m', 'foo bar'] becomes
86
+ # '-a -m "foo bar"'
87
+ def argument_string(args)
88
+ args.inject([]) do |opts, opt|
89
+ opts << (opt =~ /^-/ ? opt : opt.inspect)
90
+ end.join(' ')
91
+ end
92
+
93
+ def finish?
94
+ options.finish
95
+ end
96
+
97
+ def deliver?
98
+ options.deliver
99
+ end
100
+ end
@@ -0,0 +1,41 @@
1
+ require 'git-utils/command'
2
+
3
+ class DeleteRemoteBranch < Command
4
+
5
+ def parser
6
+ OptionParser.new do |opts|
7
+ opts.banner = "Usage: git delete-remote-branch <branch>"
8
+ opts.on("-o", "--override", "override unsafe delete") do |opt|
9
+ self.options.override = opt
10
+ end
11
+ opts.on_tail("-h", "--help", "this usage guide") do
12
+ puts opts.to_s; exit 0
13
+ end
14
+ end
15
+ end
16
+
17
+ def delete_safely?
18
+ command = "git log ..origin/#{target_branch} 2> /dev/null"
19
+ system(command) && !`#{command}`.strip.empty?
20
+ end
21
+
22
+ # Returns a command appropriate for executing at the command line
23
+ def cmd
24
+ if delete_safely? || options.override
25
+ c = ["git push origin :#{target_branch}"]
26
+ c << argument_string(unknown_options) unless unknown_options.empty?
27
+ c.join("\n")
28
+ else
29
+ $stderr.puts "Target branch contains unmerged commits."
30
+ $stderr.puts "Please cherry-pick the commits or merge the branch again."
31
+ $stderr.puts "Use -o or --override to override."
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ # Returns the name of the branch to be deleted.
38
+ def target_branch
39
+ self.known_options.first
40
+ end
41
+ end
@@ -0,0 +1,35 @@
1
+ require 'git-utils/command'
2
+
3
+ class MergeBranch < Command
4
+
5
+ def parser
6
+ OptionParser.new do |opts|
7
+ opts.banner = "Usage: git merge-branch [branch] [options]"
8
+ opts.on_tail("-h", "--help", "this usage guide") do
9
+ puts opts.to_s; exit 0
10
+ end
11
+ end
12
+ end
13
+
14
+ # Returns a command appropriate for executing at the command line
15
+ # For example:
16
+ # git checkout master
17
+ # git merge --no-ff --log <branch>
18
+ def cmd
19
+ lines = ["git checkout #{target_branch}"]
20
+ c = ["git merge --no-ff --log"]
21
+ c << argument_string(unknown_options) unless unknown_options.empty?
22
+ c << current_branch
23
+ lines << c.join(' ')
24
+ lines.join("\n")
25
+ end
26
+
27
+ private
28
+
29
+ # Returns the name of the branch to be merged into.
30
+ # If there is anything left in the known options after parsing,
31
+ # that's the merge branch. Otherwise, it's master.
32
+ def target_branch
33
+ self.known_options.first || 'master'
34
+ end
35
+ end
@@ -0,0 +1,40 @@
1
+ require 'git-utils/command'
2
+
3
+ class Open < Command
4
+
5
+ def parser
6
+ OptionParser.new do |opts|
7
+ opts.banner = "Usage: git open"
8
+ opts.on_tail("-h", "--help", "this usage guide") do
9
+ puts opts.to_s; exit 0
10
+ end
11
+ end
12
+ end
13
+
14
+ # Returns the URL for the repository page.
15
+ def page_url
16
+ if service == 'stash' && protocol == 'ssh'
17
+ pattern = /(.*)@([^:]*):?([^\/]*)\/([^\/]*)\/(.*)\.git/
18
+ replacement = 'https://\2/projects/\4/repos/\5/browse?at=' +
19
+ current_branch
20
+ elsif service == 'stash' && protocol == 'http'
21
+ pattern = /(.*)@([^:\/]*)(:?[^\/]*)\/(.*)scm\/([^\/]*)\/(.*)\.git/
22
+ replacement = 'https://\2\3/\4projects/\5/repos/\6/browse?at=' +
23
+ current_branch
24
+ elsif protocol == 'ssh'
25
+ pattern = /(.*)@(.*):(.*)\.git/
26
+ replacement = 'https://\2/\3/'
27
+ elsif protocol == 'http'
28
+ pattern = /https?\:\/\/(([^@]*)@)?(.*)\.git/
29
+ replacement = 'https://\3/'
30
+ end
31
+ origin_url.sub(pattern, replacement)
32
+ end
33
+
34
+ # Returns a command appropriate for executing at the command line
35
+ def cmd
36
+ c = ["open #{page_url}"]
37
+ c << argument_string(unknown_options) unless unknown_options.empty?
38
+ c.join("\n")
39
+ end
40
+ end
@@ -0,0 +1,39 @@
1
+ require 'optparse'
2
+
3
+ module Options
4
+
5
+ # Returns a list of options unknown to a particular options parser
6
+ # For example, if '-a' is a known option but '-b' and '-c' are not,
7
+ # unknown_options(parser, ['-a', '-b', '-c']) returns ['-b', '-c'].
8
+ # It also preserves arguments, so
9
+ # unknown_options(parser, ['-a', '-b', '-c', 'foo bar']) returns
10
+ # ['-b', '-c', 'foo bar'].
11
+ def self.unknown_options(parser, args)
12
+ unknown = []
13
+ recursive_parse = Proc.new do |arg_list|
14
+ begin
15
+ # Hack to handle an unknown '-ff' argument
16
+ # The issue here is that OptParse interprets '-ff' as a '-f' option
17
+ # applied twice. This is sort-of a feature, as it allows, e.g., '-am'
18
+ # to set both the '-a' and '-m' options, but it interacts badly
19
+ # with '-ff' (as used by 'git merge') when '-f' is one of the options.
20
+ unknown << arg_list.delete('-ff') if arg_list.include?('-ff')
21
+ parser.parse!(arg_list)
22
+ rescue OptionParser::InvalidOption => e
23
+ unknown.concat(e.args)
24
+ while !arg_list.empty? && arg_list.first[0] != "-"
25
+ unknown << arg_list.shift
26
+ end
27
+ recursive_parse.call(arg_list)
28
+ end
29
+ end
30
+ recursive_parse.call(args.dup)
31
+ unknown
32
+ end
33
+
34
+ # Returns a list of options with unknown options removed
35
+ def self.known_options(parser, args)
36
+ unknown = unknown_options(parser, args)
37
+ args.reject { |arg| unknown.include?(arg) }
38
+ end
39
+ end
@@ -0,0 +1,46 @@
1
+ require 'git-utils/command'
2
+
3
+ class PullRequest < Command
4
+
5
+ def parser
6
+ OptionParser.new do |opts|
7
+ opts.banner = "Usage: git pull-request"
8
+ opts.on_tail("-h", "--help", "this usage guide") do
9
+ puts opts.to_s; exit 0
10
+ end
11
+ end
12
+ end
13
+
14
+ # Returns the URL for a new pull request.
15
+ def new_pr_url
16
+ if service == 'stash' && protocol == 'ssh'
17
+ pattern = /(.*)@([^:]*):?([^\/]*)\/([^\/]*)\/(.*)\.git/
18
+ replacement = 'https://\2/projects/\4/repos/\5/pull-requests?create&sourceBranch=' +
19
+ current_branch
20
+ elsif service == 'stash' && protocol == 'http'
21
+ pattern = /(.*)@([^:\/]*)(:?[^\/]*)\/(.*)scm\/([^\/]*)\/(.*)\.git/
22
+ replacement = 'https://\2\3/\4projects/\5/repos/\6/pull-requests?create&sourceBranch=' +
23
+ current_branch
24
+ elsif service == 'github' && protocol == 'ssh'
25
+ pattern = /(.*)@(.*):(.*)\.git/
26
+ replacement = 'https://\2/\3/pull/new/' + current_branch
27
+ elsif service == 'github' && protocol == 'http'
28
+ pattern = /https?\:\/\/(([^@]*)@)?(.*)\.git/
29
+ replacement = 'https://\3/pull/new/' + current_branch
30
+ elsif service == 'bitbucket' && protocol == 'ssh'
31
+ pattern = /(.*)@(.*):(.*)\.git/
32
+ replacement = 'https://\2/\3/pull-request/new/'
33
+ elsif service == 'bitbucket' && protocol == 'http'
34
+ pattern = /https?\:\/\/(([^@]*)@)?(.*)\.git/
35
+ replacement = 'https://\3/pull-request/new/'
36
+ end
37
+ origin_url.sub(pattern, replacement)
38
+ end
39
+
40
+ # Returns a command appropriate for executing at the command line
41
+ def cmd
42
+ c = ["open #{new_pr_url}"]
43
+ c << argument_string(unknown_options) unless unknown_options.empty?
44
+ c.join("\n")
45
+ end
46
+ end
@@ -0,0 +1,20 @@
1
+ require 'git-utils/command'
2
+
3
+ class PushBranch < Command
4
+
5
+ def parser
6
+ OptionParser.new do |opts|
7
+ opts.banner = "Usage: git push-branch"
8
+ opts.on_tail("-h", "--help", "this usage guide") do
9
+ puts opts.to_s; exit 0
10
+ end
11
+ end
12
+ end
13
+
14
+ # Returns a command appropriate for executing at the command line
15
+ def cmd
16
+ c = ["git push origin #{current_branch}"]
17
+ c << argument_string(unknown_options) unless unknown_options.empty?
18
+ c.join("\n")
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ require 'git-utils/command'
2
+
3
+ class Switch < Command
4
+
5
+ def parser
6
+ OptionParser.new do |opts|
7
+ opts.banner = "Usage: git switch <pattern>"
8
+ opts.on_tail("-h", "--help", "this usage guide") do
9
+ puts opts.to_s; exit 0
10
+ end
11
+ end
12
+ end
13
+
14
+ # Returns the branch to switch to.
15
+ def other_branch
16
+ @other_branch ||= `git branch | grep #{pattern}`.strip
17
+ end
18
+
19
+ # Returns a command appropriate for executing at the command line
20
+ def cmd
21
+ c = ["git checkout #{other_branch}"]
22
+ c << argument_string(unknown_options) unless unknown_options.empty?
23
+ c.join("\n")
24
+ end
25
+
26
+ private
27
+
28
+ # Returns the pattern of the branch to switch to.
29
+ def pattern
30
+ self.known_options.first
31
+ end
32
+
33
+ end
@@ -0,0 +1,22 @@
1
+ require 'git-utils/command'
2
+
3
+ class Sync < Command
4
+
5
+ def parser
6
+ OptionParser.new do |opts|
7
+ opts.banner = "Usage: git sync"
8
+ opts.on_tail("-h", "--help", "this usage guide") do
9
+ puts opts.to_s; exit 0
10
+ end
11
+ end
12
+ end
13
+
14
+ # Returns a command appropriate for executing at the command line
15
+ def cmd
16
+ c = ["git checkout master"]
17
+ c << "git pull"
18
+ c << "git checkout #{current_branch}"
19
+ c << argument_string(unknown_options) unless unknown_options.empty?
20
+ c.join("\n")
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ module Git
2
+ module Utils
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Command do
4
+ let(:command) { Command.new }
5
+ subject { command }
6
+
7
+ it { should respond_to(:cmd) }
8
+ it { should respond_to(:args) }
9
+ it { should respond_to(:options) }
10
+ it { should respond_to(:parse) }
11
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe DeleteRemoteBranch do
4
+
5
+ let(:command) { DeleteRemoteBranch.new(['remote_branch']) }
6
+ before do
7
+ command.stub(:current_branch).and_return('test-br')
8
+ command.stub(:delete_safely?).and_return(true)
9
+ end
10
+ subject { command }
11
+
12
+ its(:cmd) { should match /git push origin :remote_branch/ }
13
+
14
+ describe "command-line command" do
15
+ subject { `bin/git-delete-remote-branch foobar -o --debug` }
16
+ it { should match /git push origin/ }
17
+ end
18
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe MergeBranch do
4
+
5
+ let(:command) { MergeBranch.new }
6
+ before { command.stub(:current_branch).and_return('tau-manifesto') }
7
+ subject { command }
8
+
9
+ its(:cmd) { should match /git merge/ }
10
+
11
+ shared_examples "merge-branch with known options" do
12
+ subject { command }
13
+ it "should not raise an error" do
14
+ expect { command.parse }.not_to raise_error(OptionParser::InvalidOption)
15
+ end
16
+ end
17
+
18
+ describe "with no options" do
19
+ its(:cmd) { should match /git checkout master/ }
20
+ end
21
+
22
+ describe "with a custom development branch" do
23
+ let(:command) { MergeBranch.new(['development']) }
24
+ its(:cmd) { should match /git checkout development/ }
25
+ end
26
+
27
+ describe "with some unknown options" do
28
+ let(:command) { MergeBranch.new(['dev', '-o', '-a', '-z', '--foo']) }
29
+ it_should_behave_like "merge-branch with known options"
30
+ its(:cmd) { should match /-a -z --foo/ }
31
+ end
32
+
33
+ describe "command-line command" do
34
+ subject { `bin/git-merge-branch --debug development` }
35
+ it { should match /git checkout development/ }
36
+ it { should match /git merge --no-ff --log/ }
37
+ end
38
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Open do
4
+
5
+ let(:command) { Open.new }
6
+ before do
7
+ command.stub(:current_branch).and_return('test-br')
8
+ end
9
+ subject { command }
10
+
11
+ its(:cmd) { should match /open #{command.page_url}/ }
12
+
13
+ it "should have the right page URLs" do
14
+ urls = %w[
15
+ https://mwatson@bitbucket.org/atlassian/amps.git https://bitbucket.org/atlassian/amps
16
+ git@bitbucket.org:atlassian/amps.git https://bitbucket.org/atlassian/amps
17
+ git@github.com:mhartl/git-utils.git https://github.com/mhartl/git-utils
18
+ https://github.com/mhartl/git-utils.git https://github.com/mhartl/git-utils
19
+ ssh://git@stash.atlassian.com:7999/stash/stash.git https://stash.atlassian.com/projects/stash/repos/stash/browse?at=test-br
20
+ https://mwatson@stash.atlassian.com:7990/scm/stash/stash.git https://stash.atlassian.com:7990/projects/stash/repos/stash/browse?at=test-br
21
+ ssh://git@stash.atlassian.com/stash/stash.git https://stash.atlassian.com/projects/stash/repos/stash/browse?at=test-br
22
+ https://mwatson@stash.atlassian.com/scm/stash/stash.git https://stash.atlassian.com/projects/stash/repos/stash/browse?at=test-br
23
+ https://mwatson@stash.atlassian.com/stash/scm/stash/stash.git https://stash.atlassian.com/stash/projects/stash/repos/stash/browse?at=test-br
24
+ https://mwatson@stash.atlassian.com:7990/stash/scm/stash/stash.git https://stash.atlassian.com:7990/stash/projects/stash/repos/stash/browse?at=test-br
25
+ https://example.com/repos/foobar.git https://example.com/repos/foobar
26
+ ]
27
+ urls.each_slice(2) do |origin_url, page_url|
28
+ command.stub(:origin_url).and_return(origin_url)
29
+ expect(command.page_url).to include page_url
30
+ end
31
+ end
32
+
33
+ describe "command-line command" do
34
+ subject { `bin/git-open --debug` }
35
+ it { should match /open/ }
36
+ end
37
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe PullRequest do
4
+
5
+ let(:command) { PullRequest.new }
6
+ before do
7
+ command.stub(:current_branch).and_return('test-br')
8
+ end
9
+ subject { command }
10
+
11
+ its(:cmd) { should match /open #{command.new_pr_url}/ }
12
+
13
+ it "should have the right pull request URLs" do
14
+ urls = %w[
15
+ https://mwatson@bitbucket.org/atlassian/amps.git https://bitbucket.org/atlassian/amps/pull-request/new
16
+ git@bitbucket.org:atlassian/amps.git https://bitbucket.org/atlassian/amps/pull-request/new
17
+ git@github.com:mhartl/git-utils.git https://github.com/mhartl/git-utils/pull/new/test-br
18
+ https://github.com/mhartl/git-utils.git https://github.com/mhartl/git-utils/pull/new/test-br
19
+ ssh://git@stash.atlassian.com:7999/stash/stash.git https://stash.atlassian.com/projects/stash/repos/stash/pull-requests?create&sourceBranch=test-br
20
+ https://mwatson@stash.atlassian.com:7990/scm/stash/stash.git https://stash.atlassian.com:7990/projects/stash/repos/stash/pull-requests?create&sourceBranch=test-br
21
+ ssh://git@stash.atlassian.com/stash/stash.git https://stash.atlassian.com/projects/stash/repos/stash/pull-requests?create&sourceBranch=test-br
22
+ https://mwatson@stash.atlassian.com/scm/stash/stash.git https://stash.atlassian.com/projects/stash/repos/stash/pull-requests?create&sourceBranch=test-br
23
+ https://mwatson@stash.atlassian.com/stash/scm/stash/stash.git https://stash.atlassian.com/stash/projects/stash/repos/stash/pull-requests?create&sourceBranch=test-br
24
+ https://mwatson@stash.atlassian.com:7990/stash/scm/stash/stash.git https://stash.atlassian.com:7990/stash/projects/stash/repos/stash/pull-requests?create&sourceBranch=test-br
25
+ ]
26
+
27
+ urls.each_slice(2) do |origin_url, new_pr_url|
28
+ command.stub(:origin_url).and_return(origin_url)
29
+ expect(command.new_pr_url).to include new_pr_url
30
+ end
31
+ end
32
+
33
+
34
+ describe "command-line command" do
35
+ subject { `bin/git-pull-request --debug` }
36
+ it { should match /open/ }
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe PushBranch do
4
+
5
+ let(:command) { PushBranch.new(['remote_branch']) }
6
+ before do
7
+ command.stub(:current_branch).and_return('test-br')
8
+ end
9
+ subject { command }
10
+
11
+ its(:cmd) { should match /git push origin #{command.current_branch}/ }
12
+
13
+ describe "command-line command" do
14
+ subject { `bin/git-push-branch --debug` }
15
+ it { should match /git push origin/ }
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Switch do
4
+
5
+ let(:command) { Switch.new(['other-branch']) }
6
+ before do
7
+ command.stub(:current_branch).and_return('test-br')
8
+ command.stub(:other_branch).and_return('other-branch')
9
+ end
10
+ subject { command }
11
+
12
+ its(:cmd) { should match /git checkout #{command.other_branch}/ }
13
+
14
+ describe "command-line command" do
15
+ subject { `bin/git-push-branch --debug` }
16
+ it { should match /git push origin/ }
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sync do
4
+
5
+ let(:command) { Sync.new }
6
+ before do
7
+ command.stub(:current_branch).and_return('test-br')
8
+ end
9
+ subject { command }
10
+
11
+ its(:cmd) { should match /git checkout master/ }
12
+ its(:cmd) { should match /git pull/ }
13
+ its(:cmd) { should match /git checkout #{command.current_branch}/ }
14
+
15
+ describe "command-line command" do
16
+ subject { `bin/git-sync --debug` }
17
+ it { should match /git checkout master/ }
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ require 'git-utils'
2
+
3
+ RSpec.configure do |config|
4
+ config.treat_symbols_as_metadata_keys_with_true_values = true
5
+ config.run_all_when_everything_filtered = true
6
+ config.filter_run :focus
7
+
8
+ # Disallow the old-style 'object.should' syntax.
9
+ config.expect_with :rspec do |c|
10
+ c.syntax = :expect
11
+ end
12
+ end
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ git init test-repo >/dev/null
4
+ cd test-repo
5
+ git checkout -b test-br 2>/dev/null
6
+ touch foo
7
+ git add foo
8
+ git commit -m "foo" >/dev/null
9
+ cat > open <<EOF
10
+ #!/usr/bin/env ruby
11
+
12
+ echo \$*
13
+ EOF
14
+ chmod +x open
15
+ cat > git-push-branch <<EOF
16
+ #!/usr/bin/env ruby
17
+ EOF
18
+ chmod +x git-push-branch
19
+ export PATH=`pwd`:$PATH
20
+ git remote add origin foo
21
+
22
+ test_open () {
23
+ REMOTE=$1
24
+ EXPECTED=$2
25
+ git remote set-url origin $REMOTE
26
+ RESULT=`../git-open`
27
+ if [ "$RESULT" = "$EXPECTED" ]
28
+ then
29
+ echo "passed: open $REMOTE"
30
+ else
31
+ echo "FAILED: open: Expected $EXPECTED (for origin $REMOTE) but got $RESULT"
32
+ fi
33
+ }
34
+
35
+ test_open https://mwatson@bitbucket.org/atlassian/amps.git https://bitbucket.org/atlassian/amps
36
+ test_open git@bitbucket.org:atlassian/amps.git https://bitbucket.org/atlassian/amps
37
+ test_open git@github.com:mhartl/git-utils.git https://github.com/mhartl/git-utils
38
+ test_open https://github.com/mhartl/git-utils.git https://github.com/mhartl/git-utils
39
+ test_open ssh://git@stash.atlassian.com:7999/stash/stash.git https://stash.atlassian.com/projects/stash/repos/stash/browse?at=test-br
40
+ test_open https://mwatson@stash.atlassian.com:7990/scm/stash/stash.git https://stash.atlassian.com:7990/projects/stash/repos/stash/browse?at=test-br
41
+ test_open ssh://git@stash.atlassian.com/stash/stash.git https://stash.atlassian.com/projects/stash/repos/stash/browse?at=test-br
42
+ test_open https://mwatson@stash.atlassian.com/scm/stash/stash.git https://stash.atlassian.com/projects/stash/repos/stash/browse?at=test-br
43
+ test_open https://mwatson@stash.atlassian.com/stash/scm/stash/stash.git https://stash.atlassian.com/stash/projects/stash/repos/stash/browse?at=test-br
44
+ test_open https://mwatson@stash.atlassian.com:7990/stash/scm/stash/stash.git https://stash.atlassian.com:7990/stash/projects/stash/repos/stash/browse?at=test-br
45
+
46
+ test_pr () {
47
+ REMOTE=$1
48
+ EXPECTED=$2
49
+ git remote set-url origin $REMOTE
50
+ RESULT=`../git-pull-request`
51
+ if [ "$RESULT" = "$EXPECTED" ]
52
+ then
53
+ echo "passed: pull-request $REMOTE"
54
+ else
55
+ echo "FAILED: pull-request: Expected $EXPECTED (for origin $REMOTE) but got $RESULT"
56
+ fi
57
+ }
58
+
59
+ test_pr https://mwatson@bitbucket.org/atlassian/amps.git https://bitbucket.org/atlassian/amps/pull-request/new
60
+ test_pr git@bitbucket.org:atlassian/amps.git https://bitbucket.org/atlassian/amps/pull-request/new
61
+ test_pr git@github.com:mhartl/git-utils.git https://github.com/mhartl/git-utils/pull/new/test-br
62
+ test_pr https://github.com/mhartl/git-utils.git https://github.com/mhartl/git-utils/pull/new/test-br
63
+ test_pr ssh://git@stash.atlassian.com:7999/stash/stash.git https://stash.atlassian.com/projects/stash/repos/stash/pull-requests?create\&sourceBranch=test-br
64
+ test_pr https://mwatson@stash.atlassian.com:7990/scm/stash/stash.git https://stash.atlassian.com:7990/projects/stash/repos/stash/pull-requests?create\&sourceBranch=test-br
65
+ test_pr ssh://git@stash.atlassian.com/stash/stash.git https://stash.atlassian.com/projects/stash/repos/stash/pull-requests?create\&sourceBranch=test-br
66
+ test_pr https://mwatson@stash.atlassian.com/scm/stash/stash.git https://stash.atlassian.com/projects/stash/repos/stash/pull-requests?create\&sourceBranch=test-br
67
+ test_pr https://mwatson@stash.atlassian.com/stash/scm/stash/stash.git https://stash.atlassian.com/stash/projects/stash/repos/stash/pull-requests?create\&sourceBranch=test-br
68
+ test_pr https://mwatson@stash.atlassian.com:7990/stash/scm/stash/stash.git https://stash.atlassian.com:7990/stash/projects/stash/repos/stash/pull-requests?create\&sourceBranch=test-br
69
+
70
+ cd ..
71
+ rm -rf test-repo
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: git-utils
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael Hartl
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-19 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Add some Git utilities
14
+ email:
15
+ - michael@michaelhartl.com
16
+ executables:
17
+ - git-amend
18
+ - git-anal
19
+ - git-cleanup
20
+ - git-delete-remote-branch
21
+ - git-merge-branch
22
+ - git-open
23
+ - git-polish
24
+ - git-pull-request
25
+ - git-push-branch
26
+ - git-switch
27
+ - git-sync
28
+ - git-undo
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - .DS_Store
33
+ - .gitignore
34
+ - .project_id
35
+ - .rspec
36
+ - .ruby-gemset
37
+ - .ruby-version
38
+ - Gemfile
39
+ - Gemfile.lock
40
+ - LICENSE.txt
41
+ - README.md
42
+ - Rakefile
43
+ - bin/git-amend
44
+ - bin/git-anal
45
+ - bin/git-cleanup
46
+ - bin/git-delete-remote-branch
47
+ - bin/git-merge-branch
48
+ - bin/git-open
49
+ - bin/git-polish
50
+ - bin/git-pull-request
51
+ - bin/git-push-branch
52
+ - bin/git-switch
53
+ - bin/git-sync
54
+ - bin/git-undo
55
+ - git-utils.gemspec
56
+ - lib/git-utils.rb
57
+ - lib/git-utils/command.rb
58
+ - lib/git-utils/delete_remote_branch.rb
59
+ - lib/git-utils/merge_branch.rb
60
+ - lib/git-utils/open.rb
61
+ - lib/git-utils/options.rb
62
+ - lib/git-utils/pull_request.rb
63
+ - lib/git-utils/push_branch.rb
64
+ - lib/git-utils/switch.rb
65
+ - lib/git-utils/sync.rb
66
+ - lib/git-utils/version.rb
67
+ - spec/.DS_Store
68
+ - spec/commands/.DS_Store
69
+ - spec/commands/command_spec.rb
70
+ - spec/commands/delete_remote_branch_spec.rb
71
+ - spec/commands/merge_branch_spec.rb
72
+ - spec/commands/open_spec.rb
73
+ - spec/commands/pull_request_spec.rb
74
+ - spec/commands/push_branch_spec.rb
75
+ - spec/commands/switch_spec.rb
76
+ - spec/commands/sync_spec.rb
77
+ - spec/spec_helper.rb
78
+ - test-open-pr.sh
79
+ homepage: https://github.com/mhartl/git-utils
80
+ licenses:
81
+ - MIT
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 2.0.3
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: See the README for full documentation
103
+ test_files:
104
+ - spec/.DS_Store
105
+ - spec/commands/.DS_Store
106
+ - spec/commands/command_spec.rb
107
+ - spec/commands/delete_remote_branch_spec.rb
108
+ - spec/commands/merge_branch_spec.rb
109
+ - spec/commands/open_spec.rb
110
+ - spec/commands/pull_request_spec.rb
111
+ - spec/commands/push_branch_spec.rb
112
+ - spec/commands/switch_spec.rb
113
+ - spec/commands/sync_spec.rb
114
+ - spec/spec_helper.rb