git-browse-remote 0.0.2 → 0.0.3

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: 3d53544ee6d46516eacca0cdffd3d41717e550c1
4
+ data.tar.gz: c4a7bcac64a6a4d730500c9b915cffca91a8abe5
5
+ SHA512:
6
+ metadata.gz: 50e226a70a929a512e1ae9d7bf24a9b16b2386a6f7f8c2b7d75c31e9485e056ba9fc8ce197e033895cfe7b7ce95d0551ba560b2470eabef510b910893c3a3016
7
+ data.tar.gz: 453d60928880551b838853267590d04879c8d81a123df8db78daa43ae8fbb0fa2635fa186d649ed620b62839e896ea2925fdbc77801ac260d5d4df06bc10fbc9
data/.gitignore ADDED
@@ -0,0 +1,17 @@
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
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.3
5
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in git-browse-remote.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard :rspec do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 motemen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,74 @@
1
+ git-browse-remote [![Build Status](https://travis-ci.org/motemen/git-browse-remote.png?branch=master)](https://travis-ci.org/motemen/git-browse-remote)
2
+ =================
3
+
4
+ `git-browse-remote` helps viewing remote repositories e.g. GitHub in your browser.
5
+
6
+ Usage
7
+ -----
8
+
9
+ ```
10
+ git browse-remote [-r|--remote <remote>] [--top|--rev|--ref] [-L <n> [<commit> | <remote>] [<file>]
11
+ ```
12
+
13
+ `git-browse-remote` opens your web browser by `git web--browse` to show current repository in browser.
14
+
15
+ `git-browse-remote` depends on what commit/ref `HEAD` points (or you supplied by an argument), such as:
16
+
17
+ * When on "master", opens repository's top page ("top" mode)
18
+ * e.g. opens https://github.com/motemen/git-browse-remote
19
+ * When on another branch/tag, opens repository's branch/tag page ("ref" mode)
20
+ * e.g. opens https://github.com/motemen/git-browse-remote/tree/master
21
+ * Otherwise, opens repository's commit page ("rev" mode)
22
+ * e.g. opens https://github.com/motemen/git-browse-remote/commit/04f7c64ba9d524cf311a673ddce5722b2441e2ea
23
+
24
+ As a special case, if <var>commit</var> is invalid and an valid remote name, that remore repository's page is opened.
25
+
26
+ How to set up
27
+ -------------
28
+
29
+ `git browse-remote` stores url mapping in ~/.gitconfig.
30
+ To initialize this, execute with `--init` option.
31
+
32
+ ```
33
+ % git browse-remote --init
34
+ Writing config for github.com...
35
+ Mappings generated:
36
+ browse-remote.github.com.top https://{host}/{path}
37
+ browse-remote.github.com.ref https://{host}/{path}/tree/{short_ref}
38
+ browse-remote.github.com.rev https://{host}/{path}/commit/{commit}
39
+ browse-remote.github.com.file https://{host}/{path}/blob/{short_rev}/{file}{line && "#L%d" % line}
40
+ ```
41
+
42
+ Execute `git config browse-remote.<host>.{top|ref|rev} <url template>`
43
+ to register other hosts mappings.
44
+
45
+ Especially, if you have a GitHub Enterprise repository, run
46
+
47
+ ```
48
+ % git browse-remote --init <ghe host>=github
49
+ ```
50
+
51
+ to easyly setup url mappings. `--init <host>=gitweb` is also available.
52
+
53
+ Variables available in url template
54
+ -----------------------------------
55
+
56
+ * `host` (eg. "github.com")
57
+ * `path` (eg. "motemen/git-browse-remote")
58
+ * Sliceable, subscribable like an `Array`
59
+ * `ref` (eg. "refs/heads/master")
60
+ * `short_ref` (eg. "master")
61
+ * `commit` (eg. "04f7c64ba9d524cf311a673ddce5722b2441e2ea")
62
+ * `short_commit` (eg. "04f7c64b")
63
+ * `rev` (ref or commit)
64
+ * `short_rev` (short_ref or short_commit)
65
+ * `file` (eg. "bin/git-browse-remote")
66
+ * `line` (eg. 30)
67
+
68
+ ## Contributing
69
+
70
+ 1. Fork it
71
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
72
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
73
+ 4. Push to the branch (`git push origin my-new-feature`)
74
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :spec
@@ -1,146 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # USAGE:
4
- # git browse-remote [-r|--remote <remote>] [--top|--rev|--ref] [-L <n>] [<commit>] [<file>]
5
- #
3
+ require 'git/browse/remote'
6
4
 
7
- require 'optparse'
8
-
9
- class Path < Array
10
- def to_s
11
- join('/')
12
- end
13
- end
14
-
15
- remote = nil
16
- mode = nil
17
- file = nil
18
- line = nil
19
-
20
- # Variables available:
21
- # * host (eg. "github.com")
22
- # * path - Sliceable like an Array (eg. "motemen/git-browse-remote")
23
- # * ref (eg. "refs/heads/master")
24
- # * short_ref (eg. "master")
25
- # * commit (eg. "04f7c64ba9d524cf311a673ddce5722b2441e2ea")
26
- # * short_commit (eg. "04f7c64b")
27
- # * rev (ref or commit)
28
- # * short_rev (short_ref or short_commit)
29
- # * file (eg. "bin/git-browse-remote")
30
- # * line (eg. 30)
31
- MAPPING_RECIPES = {
32
- :github => {
33
- :top => 'https://{host}/{path}',
34
- :ref => 'https://{host}/{path}/tree/{short_ref}',
35
- :rev => 'https://{host}/{path}/commit/{commit}',
36
- :file => 'https://{host}/{path}/blob/{short_rev}/{file}{line && "#L%d" % line}'
37
- },
38
-
39
- :gitweb => {
40
- :top => 'http://{host}/?p={path[-2..-1]}.git',
41
- :ref => 'http://{host}/?p={path[-2..-1]}.git;h={ref}',
42
- :rev => 'http://{host}/?p={path[-2..-1]}.git;a=commit;h={ref}',
43
- # XXX
44
- # I don't know file url of gitweb...
45
- }
46
- }
47
-
48
- OptionParser.new do |opt|
49
- opt.banner = 'git browse-remote [options] [<commit> | <remote>]'
50
- opt.on('-r', '--remote=<remote>', 'specify remote') { |r| remote = r }
51
-
52
- opt.on('--top', 'open `top` page') { mode = :top }
53
- opt.on('--rev', 'open `rev` page') { mode = :rev }
54
- opt.on('--ref', 'open `ref` page') { mode = :ref }
55
- opt.on('--init [<host>=<recipe>]', 'initialize default url mappings') do |config|
56
- if config
57
- host, name = *config.split(/=/, 2)
58
- else
59
- host, name = 'github.com', 'github'
60
- end
61
-
62
- STDERR.puts "Writing config for #{host}..."
63
-
64
- mapping = MAPPING_RECIPES[name.to_sym] or abort "Recipe '#{name}' not found"
65
- mapping.each do |mode,template|
66
- system %Q(git config --global browse-remote.#{host}.#{mode} '#{template}')
67
- end
68
-
69
- STDERR.puts 'Mappings generated:'
70
- exec "git config --get-regexp ^browse-remote\\.#{host}\\."
71
- end
72
- opt.on('-L <n>', 'specify line number (only meaningful on file mode)', Integer) { |n| line = n }
73
- opt.parse!(ARGV)
74
- end
75
-
76
- target, file = *ARGV[0..1]
77
-
78
- # handle git browse-remote <file>
79
- # TODO file exists and also is a valid ref/origin
80
- if target && !file && File.exists?(target)
81
- target, file = nil, target
82
- end
83
-
84
- target ||= `git symbolic-ref -q HEAD`[/.+/]
85
-
86
- if target
87
- if `git rev-parse --verify --quiet #{target}` && $? == 0
88
- # valid ref
89
- ref = `git rev-parse --symbolic-full-name #{target}`.chomp
90
- elsif `git config --get remote.#{target}.url`.chomp.empty? == false
91
- # not a valid ref, but valid remote
92
- ref = 'master'
93
- remote = target
94
- target = nil
95
- else
96
- abort "Not a valid ref or remote: #{target}"
97
- end
98
- else
99
- ref = `git name-rev --name-only HEAD`.chomp.sub(%r(\^0$), '') # some workaround for ^0
100
- end
101
-
102
- if ref == 'HEAD'
103
- ref = nil
104
- short_ref = nil
105
- else
106
- short_ref = ref.sub(%r(^refs/), '')
107
-
108
- if short_ref.sub!(%r(^heads/), '')
109
- mode ||= short_ref == 'master' ? :top : :ref
110
- end
111
-
112
- if short_ref.sub!(%r(^tags/), '')
113
- mode ||= :ref
114
- end
115
-
116
- if short_ref.sub!(%r(^remotes/([^/]+)/), '')
117
- remote ||= $1
118
- mode ||= short_ref == 'master' ? :top : :ref
119
- end
120
-
121
- end
122
-
123
- commit = `git rev-parse #{target || 'HEAD'}`.chomp
124
- short_commit = `git rev-parse --short #{target || 'HEAD'}`.chomp
125
-
126
- # `rev` will be raw sha1 if --rev is specified explicitly
127
- rev = mode == :rev ? commit : ref || commit
128
- short_rev = mode == :rev ? short_commit : short_ref || short_commit
129
-
130
- mode = :file if file
131
-
132
- remote ||= 'origin'
133
- mode ||= :rev
134
-
135
- remote_url = `git config remote.#{remote}.url`[/.+/] or
136
- abort "Could not get remote url: #{remote}"
137
-
138
- host, *path = remote_url.sub(%r(^\w+://), '').sub(/^[\w-]+@/, '').split(/[\/:]+/)
139
- path.last.sub!(/\.git$/, '')
140
- path = Path.new(path)
141
-
142
- template = `git config --get browse-remote.#{host}.#{mode}`[/.+/] or
143
- abort "No '#{mode}' mapping found for #{host} (maybe `git browse-remote --init` required)"
144
-
145
- url = template.gsub(/\{(.+?)\}/) { |m| eval($1) }
146
- exec 'git', 'web--browse', url
5
+ Git::Browse::Remote::Runner.new(ARGV).run
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'git/browse/remote/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "git-browse-remote"
8
+ spec.version = Git::Browse::Remote::VERSION
9
+ spec.authors = ["motemen"]
10
+ spec.email = ["motemen@gmail.com"]
11
+ spec.summary = 'Open web browser to view remote Git repositories'
12
+ spec.homepage = 'https://github.com/motemen/git-browse-remote'
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.3"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency 'rspec', '~> 2'
23
+ spec.add_development_dependency 'simplecov', '0.7.1'
24
+ spec.add_development_dependency 'guard'
25
+ spec.add_development_dependency 'guard-rspec'
26
+ spec.add_development_dependency 'terminal-notifier-guard'
27
+ end
@@ -0,0 +1,2 @@
1
+ require 'git/browse/remote/version'
2
+ require 'git/browse/remote/runner'
@@ -0,0 +1,176 @@
1
+ require 'git/browse/remote/git'
2
+
3
+ module Git::Browse::Remote
4
+ class Path < Array
5
+ def to_s
6
+ join('/')
7
+ end
8
+ end
9
+
10
+ class Core
11
+ attr_accessor :line, :file
12
+
13
+ MAPPING_RECIPES = {
14
+ :github => {
15
+ :top => 'https://{host}/{path}',
16
+ :ref => 'https://{host}/{path}/tree/{short_ref}',
17
+ :rev => 'https://{host}/{path}/commit/{commit}',
18
+ :file => 'https://{host}/{path}/blob/{short_rev}/{file}{line && "#L%d" % line}'
19
+ },
20
+
21
+ :gitweb => {
22
+ :top => 'http://{host}/?p={path[-2..-1]}.git',
23
+ :ref => 'http://{host}/?p={path[-2..-1]}.git;h={ref}',
24
+ :rev => 'http://{host}/?p={path[-2..-1]}.git;a=commit;h={ref}',
25
+ # XXX
26
+ # I don't know file url of gitweb...
27
+ }
28
+ }
29
+
30
+ def template_type
31
+ if @file
32
+ :file
33
+ else
34
+ mode
35
+ end
36
+ end
37
+
38
+ def init!(host, name)
39
+ mapping = MAPPING_RECIPES[name] or abort "Recipe '#{name}' not found"
40
+ mapping.each do |mode,template|
41
+ system %Q(git config --global browse-remote.#{host}.#{mode} '#{template}')
42
+ end
43
+ end
44
+
45
+ def url
46
+ if target && !@file && File.exists?(target)
47
+ self.target, @file = nil, target
48
+ end
49
+
50
+ if target
51
+ if Git.is_valid_rev? target
52
+ @ref = Git.full_name_of_rev(target)
53
+ elsif Git.is_valid_remote? target
54
+ @remote, @ref = target, 'master'
55
+ else
56
+ abort "Not a valid ref or remote: #{target}"
57
+ end
58
+ else
59
+ @ref = Git.symbolic_name_of_head
60
+ end
61
+
62
+ if @ref == 'HEAD'
63
+ @ref = nil
64
+ end
65
+
66
+ remote_url = `git config remote.#{remote}.url`[/.+/] or
67
+ abort "Could not get remote url: #{remote}"
68
+
69
+ host, *path = remote_url.sub(%r(^\w+://), '').sub(/^[\w-]+@/, '').split(/[\/:]+/)
70
+ path.last.sub!(/\.git$/, '')
71
+ path = Path.new(path)
72
+
73
+ template = `git config --get browse-remote.#{host}.#{template_type}`[/.+/]
74
+ if not template and host == 'github.com'
75
+ template = MAPPING_RECIPES[:github][template_type.to_sym] or
76
+ abort "No '#{template_type}' mapping found for #{host} (maybe `git browse-remote --init` required)"
77
+ end
78
+
79
+ url = template.gsub(/\{(.+?)\}/) { |m| eval($1) }
80
+ end
81
+
82
+ def remote=(remote)
83
+ @remote = remote
84
+ end
85
+
86
+ def remote
87
+ return @remote if @remote
88
+
89
+ if ref
90
+ if ref.match(%r<^(?:refs/)?remotes/([^/]+)/>)
91
+ @remote = $1
92
+ end
93
+ end
94
+
95
+ @remote ||= 'origin'
96
+ end
97
+
98
+ def mode=(mode)
99
+ @mode = mode
100
+ end
101
+
102
+ def mode
103
+ return @mode if @mode
104
+
105
+ if ref
106
+ if ref.match(%r<^((?:refs/)?(?:heads|remotes/[^/]+)/)?master$>)
107
+ @mode = :top
108
+ elsif ref.match(%r<^(?:refs/)?(?:heads|tags|remotes)/>)
109
+ @mode = :ref
110
+ end
111
+ end
112
+
113
+ @mode || :rev
114
+ end
115
+
116
+ def _commit(short = false)
117
+ if short
118
+ Git.parse_rev_short(target || 'HEAD')
119
+ else
120
+ Git.parse_rev(target || 'HEAD')
121
+ end
122
+ end
123
+
124
+ def commit
125
+ _commit
126
+ end
127
+
128
+ def short_commit
129
+ _commit(true)
130
+ end
131
+
132
+ def _rev(short = false)
133
+ if mode == :rev
134
+ _commit(short)
135
+ else
136
+ _ref(short) || _commit(short)
137
+ end
138
+ end
139
+
140
+ def rev
141
+ _rev
142
+ end
143
+
144
+ def short_rev
145
+ _rev(true)
146
+ end
147
+
148
+ def _ref(short = false)
149
+ if short
150
+ @ref.sub(%r(^refs/), '').
151
+ sub(%r(^heads/), '').
152
+ sub(%r(^tags/), '').
153
+ sub(%r(^remotes/([^/]+)/), '')
154
+ else
155
+ @ref
156
+ end
157
+ end
158
+
159
+ def ref
160
+ _ref
161
+ end
162
+
163
+ def short_ref
164
+ _ref(true)
165
+ end
166
+
167
+ def target=(target)
168
+ @target = target
169
+ end
170
+
171
+ def target
172
+ return @target if @target
173
+ @target ||= Git.resolved_head
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,36 @@
1
+ module Git::Browse::Remote
2
+ module Git
3
+ def self.is_valid_rev?(target)
4
+ `git rev-parse --verify --quiet #{target}` && $? == 0
5
+ end
6
+
7
+ def self.is_valid_remote?(remote)
8
+ `git config --get remote.#{remote}.url`.chomp.empty? == false
9
+ end
10
+
11
+ def self.parse_rev(ref)
12
+ `git rev-parse #{ref}`.chomp
13
+ end
14
+
15
+ def self.parse_rev_short(ref)
16
+ `git rev-parse --short #{ref}`.chomp
17
+ end
18
+
19
+ def self.full_name_of_rev(rev)
20
+ `git rev-parse --symbolic-full-name #{rev}`[/.+/] or `git rev-parse --symbolic-full-name #{name_rev(rev)}`[/.+/]
21
+ end
22
+
23
+ # the ref whom HEAD points to
24
+ def self.resolved_head
25
+ `git symbolic-ref -q HEAD`[/.+/]
26
+ end
27
+
28
+ def self.name_rev(rev)
29
+ `git name-rev --name-only #{rev}`.chomp
30
+ end
31
+
32
+ def self.symbolic_name_of_head
33
+ name_rev('HEAD').sub(%r(\^0$), '') # some workaround for ^0
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,49 @@
1
+ require 'git/browse/remote/core'
2
+ require 'git/browse/remote/version'
3
+ require 'optparse'
4
+
5
+ module Git::Browse::Remote
6
+ class Runner
7
+ def initialize(args)
8
+ @args = args
9
+ @core = Core.new
10
+ end
11
+
12
+ def run
13
+ OptionParser.new do |opt|
14
+ opt.banner = 'git browse-remote [options] [<commit> | <remote>] [--] [<file>]'
15
+ opt.version = VERSION
16
+ opt.on('-r', '--remote=<remote>', 'specify remote') { |r| @core.remote = r }
17
+
18
+ opt.on('--stdout', 'prints URL instead of opening browser') { @stdout = true }
19
+
20
+ opt.on('--top', 'open `top` page') { @core.mode = :top }
21
+ opt.on('--rev', 'open `rev` page') { @core.mode = :rev }
22
+ opt.on('--ref', 'open `ref` page') { @core.mode = :ref }
23
+ opt.on('--init [<host>=<recipe>]', 'initialize default url mappings') do |config|
24
+ if config
25
+ host, name = *config.split(/=/, 2)
26
+ else
27
+ host, name = 'github.com', 'github'
28
+ end
29
+
30
+ STDERR.puts "Writing config for #{host}..."
31
+
32
+ @core.init!(host, name.to_sym)
33
+
34
+ STDERR.puts 'Mappings generated:'
35
+ exec "git config --get-regexp ^browse-remote\\.#{host}\\."
36
+ end
37
+ opt.on('-L <n>', 'specify line number (only meaningful on file mode)', Integer) { |n| @core.line = n }
38
+ end.parse!(@args)
39
+
40
+ @core.target, @core.file = *@args[0..1]
41
+
42
+ if @stdout
43
+ puts @core.url
44
+ else
45
+ exec 'git', 'web--browse', @core.url
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,7 @@
1
+ module Git
2
+ module Browse
3
+ module Remote
4
+ VERSION = '0.0.3'
5
+ end
6
+ end
7
+ end
data/spec/bin_spec.rb ADDED
@@ -0,0 +1,333 @@
1
+ require 'rspec'
2
+ require 'tmpdir'
3
+ require 'pathname'
4
+ require 'open3'
5
+
6
+ unless ENV['CI']
7
+ require 'simplecov'
8
+
9
+ module SimpleCov
10
+ def self.lap
11
+ if running
12
+ @result = SimpleCov::Result.new(Coverage.result.merge_resultset(SimpleCov::ResultMerger.merged_result.original_result))
13
+ SimpleCov::ResultMerger.store_result(@result)
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ ROOT = Pathname.new(__FILE__).parent.parent
20
+
21
+ $:.unshift ROOT.join('lib').to_s
22
+
23
+ require 'git/browse/remote'
24
+
25
+ def git(*args)
26
+ if Open3.methods(false).include? :capture3
27
+ out, err, status = Open3.capture3('git', *args.map { |arg| arg.to_s })
28
+ else
29
+ out = `git #{args.map { |arg| arg.to_s.shellescape }.join(' ')}`
30
+ status = $?.to_i
31
+ end
32
+
33
+ if status != 0
34
+ abort "git #{args.join(' ')} failed: #{err}"
35
+ end
36
+
37
+ out[/.*/]
38
+ end
39
+
40
+ RSpec.configure do |config|
41
+ config.before(:all) do
42
+ @pwd = Dir.pwd
43
+ @tmpdir = Dir.mktmpdir
44
+ Dir.chdir @tmpdir
45
+
46
+ @sha1 = {}
47
+
48
+ git :init
49
+
50
+ git :config, '--local', 'user.email', 'gbr@example.com'
51
+ git :config, '--local', 'user.name', 'git-browse-remote tester'
52
+
53
+ git :remote, 'add', 'origin', 'https://github.com/user/repo.git'
54
+ git :remote, 'add', 'origin2', 'git@gh-mirror.host:user/repo2'
55
+
56
+ FileUtils.copy_file ROOT + 'README.md', 'README.md'
57
+ git :add, 'README.md'
58
+ git :commit, '-m' '1st commit'
59
+
60
+ git :commit, '-m' '2nd commit', '--allow-empty'
61
+
62
+ git :checkout, '-b', 'branch-1'
63
+
64
+ git :commit, '-m' 'branched commit', '--allow-empty'
65
+ @sha1[:'branch-1'] = git 'rev-parse', 'HEAD'
66
+
67
+ git :checkout, 'master'
68
+
69
+ git :commit, '-m' '3rd commit (tagged)', '--allow-empty'
70
+ git :tag, 'tag-a'
71
+
72
+ git :commit, '-m' '4th commit (remote HEAD)', '--allow-empty'
73
+ git :remote, 'add', 'local-remote', '.git'
74
+ git :remote, 'update', 'local-remote'
75
+ git :remote, 'set-url', 'local-remote', 'https://github.com/user/repo3.git'
76
+
77
+ git :commit, '-m' '5th commit', '--allow-empty'
78
+ @sha1[:'master~1'] = git 'rev-parse', 'HEAD'
79
+
80
+ git :commit, '-m' '6th commit', '--allow-empty'
81
+ @sha1[:master] = git 'rev-parse', 'HEAD'
82
+
83
+ # system 'git log --abbrev-commit --oneline --decorate --graph --all'
84
+ # the commit graph looks like below;
85
+ #
86
+ # * e6b5d6f (local-remote/branch-1, branch-1) branched commit
87
+ # | * 03b1d4d (HEAD, master) 6th commit
88
+ # | * b591899 5th commit
89
+ # | * 3139e94 (local-remote/master) 4th commit (remote HEAD)
90
+ # | * a423770 (tag: tag-a) 3rd commit (tagged)
91
+ # |/
92
+ # * 06f6ebb 2nd commit
93
+ # * ff7b92b 1st commit
94
+ end
95
+
96
+ config.after(:all) do
97
+ FileUtils.remove_entry @tmpdir
98
+ Dir.chdir @pwd
99
+ end
100
+
101
+ config.after(:each) do
102
+ git :checkout, 'master'
103
+ end
104
+ end
105
+
106
+ module Kernel
107
+ def exec(*args)
108
+ $exec_args = args
109
+ end
110
+ end
111
+
112
+ def git_browse_remote(args)
113
+ begin
114
+ SimpleCov.start unless ENV['CI']
115
+ ARGV.replace(args)
116
+ load ROOT + 'bin/git-browse-remote', true
117
+ true
118
+ rescue
119
+ false
120
+ ensure
121
+ SimpleCov.lap unless ENV['CI']
122
+ end
123
+ end
124
+
125
+ def opened_url
126
+ git_browse_remote(args)
127
+
128
+ if $exec_args[0..1] == [ 'git', 'web--browse' ]
129
+ $exec_args[2]
130
+ else
131
+ nil
132
+ end
133
+ end
134
+
135
+ def when_run_with_args(*args, &block)
136
+ context "when run with args #{args}" do
137
+ let(:args) { args }
138
+ instance_eval(&block)
139
+ end
140
+ end
141
+
142
+ describe 'git-browse-remote' do
143
+ when_run_with_args '--init', 'gh-mirror.host=github' do
144
+ it 'should run successfully' do
145
+ expect(git_browse_remote(args)).to be(true)
146
+ end
147
+ end
148
+
149
+ context 'when on master' do
150
+ when_run_with_args do
151
+ it 'should open top page' do
152
+ expect(opened_url).to eq("https://github.com/user/repo")
153
+ end
154
+ end
155
+
156
+ when_run_with_args '--top' do
157
+ it 'should open top page' do
158
+ expect(opened_url).to eq("https://github.com/user/repo")
159
+ end
160
+ end
161
+
162
+ when_run_with_args '--rev' do
163
+ it 'should open rev page' do
164
+ expect(opened_url).to eq("https://github.com/user/repo/commit/#{@sha1[:master]}")
165
+ end
166
+ end
167
+
168
+ when_run_with_args '--ref' do
169
+ it 'should open ref page' do
170
+ expect(opened_url).to eq("https://github.com/user/repo/tree/master")
171
+ end
172
+ end
173
+
174
+ when_run_with_args 'HEAD~1' do
175
+ it 'should open previous rev\'s page' do
176
+ expect(opened_url).to eq("https://github.com/user/repo/commit/#{@sha1[:'master~1']}")
177
+ end
178
+ end
179
+
180
+ when_run_with_args 'master' do
181
+ it 'should open top page' do
182
+ expect(opened_url).to eq("https://github.com/user/repo")
183
+ end
184
+ end
185
+
186
+ when_run_with_args '--', 'README.md' do
187
+ it 'should open the file page' do
188
+ expect(opened_url).to eq("https://github.com/user/repo/blob/master/README.md")
189
+ end
190
+ end
191
+
192
+ when_run_with_args '--rev', '--', 'README.md' do
193
+ it 'should open the file page by revision' do
194
+ expect(opened_url).to eq("https://github.com/user/repo/blob/#{@sha1[:master][0..6]}/README.md")
195
+ end
196
+ end
197
+
198
+ when_run_with_args '-L3', '--', 'README.md' do
199
+ it 'should open the file at the specified line' do
200
+ expect(opened_url).to eq("https://github.com/user/repo/blob/master/README.md#L3")
201
+ end
202
+ end
203
+
204
+ when_run_with_args 'branch-1' do
205
+ it 'should open the branch page' do
206
+ expect(opened_url).to eq("https://github.com/user/repo/tree/branch-1")
207
+ end
208
+ end
209
+
210
+ when_run_with_args '--rev', 'branch-1' do
211
+ it 'should open the rev page of the branch' do
212
+ expect(opened_url).to eq("https://github.com/user/repo/commit/#{@sha1[:'branch-1']}")
213
+ end
214
+ end
215
+
216
+ when_run_with_args '--remote', 'origin2' do
217
+ it 'should open the specified remote page' do
218
+ expect(opened_url).to eq("https://gh-mirror.host/user/repo2")
219
+ end
220
+ end
221
+
222
+ when_run_with_args '-r', 'origin2' do
223
+ it 'should open the specified remote page' do
224
+ expect(opened_url).to eq("https://gh-mirror.host/user/repo2")
225
+ end
226
+ end
227
+
228
+ when_run_with_args '-r', 'origin2', '--rev' do
229
+ it 'should open the specified remote page and the revision' do
230
+ expect(opened_url).to eq("https://gh-mirror.host/user/repo2/commit/#{@sha1[:master]}")
231
+ end
232
+ end
233
+
234
+ when_run_with_args 'README.md' do
235
+ it 'should open file file page' do
236
+ expect(opened_url).to eq("https://github.com/user/repo/blob/master/README.md")
237
+ end
238
+ end
239
+
240
+ when_run_with_args 'origin2' do
241
+ it 'should open the remote' do
242
+ expect(opened_url).to eq("https://gh-mirror.host/user/repo2")
243
+ end
244
+ end
245
+ end
246
+
247
+ context 'on some branch' do
248
+ before { git :checkout, 'branch-1' }
249
+
250
+ when_run_with_args do
251
+ it 'should open the current branch page' do
252
+ expect(opened_url).to eq("https://github.com/user/repo/tree/branch-1")
253
+ end
254
+ end
255
+
256
+ when_run_with_args '--top' do
257
+ it 'should open the top page' do
258
+ expect(opened_url).to eq("https://github.com/user/repo")
259
+ end
260
+ end
261
+
262
+ when_run_with_args '--rev' do
263
+ it 'should open the revision page of current HEAD' do
264
+ expect(opened_url).to eq("https://github.com/user/repo/commit/#{@sha1[:'branch-1']}")
265
+ end
266
+ end
267
+
268
+ when_run_with_args 'README.md' do
269
+ it 'should open the file page at current branch' do
270
+ expect(opened_url).to eq("https://github.com/user/repo/blob/branch-1/README.md")
271
+ end
272
+ end
273
+ end
274
+
275
+ context 'on detached HEAD' do
276
+ before { git :checkout, 'HEAD~1' }
277
+
278
+ when_run_with_args do
279
+ it 'should open the revision page' do
280
+ expect(opened_url).to eq("https://github.com/user/repo/commit/#{@sha1[:'master~1']}")
281
+ end
282
+ end
283
+
284
+ when_run_with_args 'HEAD' do
285
+ it 'should open the revision page' do
286
+ expect(opened_url).to eq("https://github.com/user/repo/commit/#{@sha1[:'master~1']}")
287
+ end
288
+ end
289
+ end
290
+
291
+ context 'on tag' do
292
+ before { git :checkout, 'tag-a' }
293
+
294
+ when_run_with_args do
295
+ it 'should open the tag page' do
296
+ expect(opened_url).to eq("https://github.com/user/repo/tree/tag-a")
297
+ end
298
+ end
299
+ end
300
+
301
+ context 'on remote branch' do
302
+ before { git :checkout, 'local-remote/master' }
303
+
304
+ when_run_with_args do
305
+ it 'should open the remote' do
306
+ expect(opened_url).to eq("https://github.com/user/repo3")
307
+ end
308
+ end
309
+ end
310
+
311
+ context 'after changing branches' do
312
+ before {
313
+ git :checkout, 'branch-1'
314
+ git :checkout, 'master'
315
+ }
316
+
317
+ when_run_with_args 'HEAD@{1}' do
318
+ it 'should open the previous branch page' do
319
+ expect(opened_url).to eq("https://github.com/user/repo/tree/branch-1")
320
+ end
321
+ end
322
+
323
+ when_run_with_args '--ref', 'HEAD@{1}' do
324
+ it 'should open the previous branch page' do
325
+ expect(opened_url).to eq("https://github.com/user/repo/tree/branch-1")
326
+ end
327
+ end
328
+ end
329
+
330
+ it 'should abort on invalid ref' do
331
+ expect { git_browse_remote([ 'xxx-nonexistent-ref' ]) }.to raise_error SystemExit
332
+ end
333
+ end
@@ -0,0 +1,159 @@
1
+ require 'git/browse/remote/core'
2
+
3
+ describe Git::Browse::Remote::Core do
4
+ subject(:core) { Git::Browse::Remote::Core.new }
5
+
6
+ describe '#template_type' do
7
+ context 'if @file set' do
8
+ before { core.file = 'README.md' }
9
+
10
+ it 'is :file' do
11
+ expect(core.template_type).to be(:file)
12
+ end
13
+ end
14
+
15
+ context 'if @file not set' do
16
+ it 'is same as #mode' do
17
+ expect(core.template_type).to be(core.mode)
18
+ end
19
+ end
20
+ end
21
+
22
+ describe '#remote' do
23
+ context 'by default' do
24
+ it 'is "origin"' do
25
+ expect(core.remote).to eq('origin')
26
+ end
27
+ end
28
+
29
+ context 'if remote set' do
30
+ before { core.remote = 'remote-foo' }
31
+
32
+ it 'is the set value' do
33
+ expect(core.remote).to eq('remote-foo')
34
+ end
35
+ end
36
+
37
+ context 'if #ref is a remote ref' do
38
+ it 'is the remote name in the ref' do
39
+ core.stub(:ref).and_return('remotes/remote-bar/master')
40
+ expect(core.remote).to eq('remote-bar')
41
+ end
42
+ end
43
+ end
44
+
45
+ describe '#mode' do
46
+ context 'by default' do
47
+ it 'is :rev' do
48
+ expect(core.mode).to eq(:rev)
49
+ end
50
+ end
51
+
52
+ context 'if mode set' do
53
+ before { core.mode = :foo }
54
+
55
+ it 'is the set value' do
56
+ expect(core.mode).to eq(:foo)
57
+ end
58
+ end
59
+
60
+ context 'if #ref points to master' do
61
+ it 'is :top' do
62
+ core.stub(:ref).and_return('refs/heads/master')
63
+ expect(core.mode).to eq(:top)
64
+ end
65
+ end
66
+
67
+ context 'if #ref points to some ref' do
68
+ it 'is :ref' do
69
+ core.stub(:ref).and_return('refs/heads/branch-a')
70
+ expect(core.mode).to eq(:ref)
71
+ end
72
+ end
73
+ end
74
+
75
+ describe '#commit' do
76
+ it 'calls Git.parse_rev with #target' do
77
+ Git::Browse::Remote::Git.should_receive(:parse_rev).with('__TARGET__').and_return('0000000000000000000000000000000000000000')
78
+ core.stub(:target).and_return('__TARGET__')
79
+ expect(core.commit).to eq('0000000000000000000000000000000000000000')
80
+ end
81
+ end
82
+
83
+ describe '#short_commit' do
84
+ it 'calls Git.parse_rev_short with #target' do
85
+ Git::Browse::Remote::Git.should_receive(:parse_rev_short).with('__TARGET__').and_return('00000000')
86
+ core.stub(:target).and_return('__TARGET__')
87
+ expect(core.short_commit).to eq('00000000')
88
+ end
89
+ end
90
+
91
+ describe '#_rev' do
92
+ context 'if #mode is :ref (not :rev)' do
93
+ before { core.stub(:mode).and_return(:ref) }
94
+
95
+ context 'and #_ref is not defined' do
96
+ it 'returns the #_commit value' do
97
+ core.should_receive(:_commit).and_return('0000000000000000000000000000000000000000')
98
+ expect(core._rev).to eq('0000000000000000000000000000000000000000')
99
+ end
100
+ end
101
+
102
+ context 'and #_ref is defined' do
103
+ it 'returns the #_ref value' do
104
+ core.should_receive(:_ref).and_return('a-ref')
105
+ expect(core._rev).to eq('a-ref')
106
+ end
107
+ end
108
+ end
109
+
110
+ context 'if #mode is :rev' do
111
+ before { core.stub(:mode).and_return(:rev) }
112
+
113
+ context 'and #_ref is not defined' do
114
+ it 'returns the #_commit value' do
115
+ core.should_receive(:_commit).and_return('0000000000000000000000000000000000000000')
116
+ expect(core._rev).to eq('0000000000000000000000000000000000000000')
117
+ end
118
+ end
119
+
120
+ context 'and #_ref is defined' do
121
+ it 'returns the #_commit value though' do
122
+ core.stub(:_ref).and_return('a-ref')
123
+ core.should_receive(:_commit).and_return('0000000000000000000000000000000000000000')
124
+ expect(core._rev).to eq('0000000000000000000000000000000000000000')
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ describe '#short_ref' do
131
+ context 'if @ref is "refs/heads/master"' do
132
+ before { core.instance_variable_set :@ref, 'refs/heads/master' }
133
+ it 'should be "master"' do
134
+ expect(core.short_ref).to eq('master')
135
+ end
136
+ end
137
+
138
+ context 'if @ref is "refs/heads/fix/some/bug"' do
139
+ before { core.instance_variable_set :@ref, 'refs/heads/fix/some/bug' }
140
+ it 'should be "fix/some/bug"' do
141
+ expect(core.short_ref).to eq('fix/some/bug')
142
+ end
143
+ end
144
+
145
+ context 'if @ref is "refs/tags/v0.0.1"' do
146
+ before { core.instance_variable_set :@ref, 'refs/tags/v0.0.1' }
147
+ it 'should be "v0.0.1"' do
148
+ expect(core.short_ref).to eq('v0.0.1')
149
+ end
150
+ end
151
+
152
+ context 'if @ref is "refs/remotes/origin/foo/bar"' do
153
+ before { core.instance_variable_set :@ref, 'refs/remotes/origin/foo/bar' }
154
+ it 'should be "foo/bar"' do
155
+ expect(core.short_ref).to eq('foo/bar')
156
+ end
157
+ end
158
+ end
159
+ end
metadata CHANGED
@@ -1,47 +1,162 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-browse-remote
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
5
- prerelease:
4
+ version: 0.0.3
6
5
  platform: ruby
7
6
  authors:
8
7
  - motemen
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-10-05 00:00:00.000000000 Z
13
- dependencies: []
11
+ date: 2013-10-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.7.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 0.7.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: terminal-notifier-guard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
14
111
  description:
15
- email: motemen@gmail.com
112
+ email:
113
+ - motemen@gmail.com
16
114
  executables:
17
115
  - git-browse-remote
18
116
  extensions: []
19
117
  extra_rdoc_files: []
20
118
  files:
119
+ - .gitignore
120
+ - .travis.yml
121
+ - Gemfile
122
+ - Guardfile
123
+ - LICENSE.txt
124
+ - README.md
125
+ - Rakefile
21
126
  - bin/git-browse-remote
127
+ - git-browse-remote.gemspec
128
+ - lib/git/browse/remote.rb
129
+ - lib/git/browse/remote/core.rb
130
+ - lib/git/browse/remote/git.rb
131
+ - lib/git/browse/remote/runner.rb
132
+ - lib/git/browse/remote/version.rb
133
+ - spec/bin_spec.rb
134
+ - spec/lib/git/browse/remote/core_spec.rb
22
135
  homepage: https://github.com/motemen/git-browse-remote
23
- licenses: []
136
+ licenses:
137
+ - MIT
138
+ metadata: {}
24
139
  post_install_message:
25
140
  rdoc_options: []
26
141
  require_paths:
27
142
  - lib
28
143
  required_ruby_version: !ruby/object:Gem::Requirement
29
- none: false
30
144
  requirements:
31
- - - ! '>='
145
+ - - '>='
32
146
  - !ruby/object:Gem::Version
33
147
  version: '0'
34
148
  required_rubygems_version: !ruby/object:Gem::Requirement
35
- none: false
36
149
  requirements:
37
- - - ! '>='
150
+ - - '>='
38
151
  - !ruby/object:Gem::Version
39
152
  version: '0'
40
153
  requirements: []
41
154
  rubyforge_project:
42
- rubygems_version: 1.8.21
155
+ rubygems_version: 2.0.3
43
156
  signing_key:
44
- specification_version: 3
157
+ specification_version: 4
45
158
  summary: Open web browser to view remote Git repositories
46
- test_files: []
159
+ test_files:
160
+ - spec/bin_spec.rb
161
+ - spec/lib/git/browse/remote/core_spec.rb
47
162
  has_rdoc: