between_meals 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,39 +1,50 @@
1
1
  # vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
2
2
 
3
3
  # Copyright 2013-present Facebook
4
- #
4
+ #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
6
6
  # you may not use this file except in compliance with the License.
7
7
  # You may obtain a copy of the License at
8
- #
8
+ #
9
9
  # http://www.apache.org/licenses/LICENSE-2.0
10
- #
10
+ #
11
11
  # Unless required by applicable law or agreed to in writing, software
12
12
  # distributed under the License is distributed on an "AS IS" BASIS,
13
13
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
14
  # See the License for the specific language governing permissions and
15
15
  # limitations under the License.
16
16
 
17
+ # Require openssl in order to make rugged work reliably
18
+ require 'openssl'
19
+
17
20
  require 'rugged'
18
21
  require 'mixlib/shellout'
19
22
  require 'between_meals/changeset'
23
+ require 'between_meals/repo/git/cmd'
20
24
 
21
25
  module BetweenMeals
22
- # Local checkout wrapper
23
26
  class Repo
24
- # Git provider
25
27
  class Git < BetweenMeals::Repo
26
28
  def setup
27
29
  if File.exists?(File.expand_path(@repo_path))
28
- @repo = Rugged::Repository.new(File.expand_path(@repo_path))
30
+ begin
31
+ @repo = Rugged::Repository.new(File.expand_path(@repo_path))
32
+ rescue
33
+ @repo = nil
34
+ end
29
35
  else
30
36
  @repo = nil
31
37
  end
32
38
  @bin = 'git'
39
+ @cmd = BetweenMeals::Repo::Git::Cmd.new(
40
+ :bin => @bin,
41
+ :cwd => @repo_path,
42
+ :logger => @logger,
43
+ )
33
44
  end
34
45
 
35
46
  def exists?
36
- @repo && !@repo.empty?
47
+ !@repo.nil?
37
48
  end
38
49
 
39
50
  def head_rev
@@ -58,34 +69,30 @@ module BetweenMeals
58
69
  end
59
70
 
60
71
  def head_parents
61
- @repo.head.target.parents
72
+ @repo.head.target.parents.map do |x|
73
+ { :rev => x.tree.oid, :time => x.time }
74
+ end
62
75
  end
63
76
 
64
77
  def checkout(url)
65
- s = Mixlib::ShellOut.new(
66
- "#{@bin} clone #{url} #{@repo} #{@repo_path}"
67
- ).run_command
68
- s.error!
78
+ @cmd.clone(url, @repo_path)
69
79
  @repo = Rugged::Repository.new(File.expand_path(@repo_path))
70
80
  end
71
81
 
72
82
  # Return files changed between two revisions
73
83
  def changes(start_ref, end_ref)
74
- check_refs(start_ref, end_ref)
75
- s = Mixlib::ShellOut.new(
76
- "#{@bin} diff --name-status #{start_ref} #{end_ref}",
77
- :cwd => File.expand_path(@repo_path)
78
- )
79
- s.run_command.error!
84
+ valid_ref?(start_ref)
85
+ valid_ref?(end_ref) if end_ref
86
+ stdout = @cmd.diff(start_ref, end_ref).stdout
80
87
  begin
81
- parse_status(s.stdout).compact
88
+ parse_status(stdout).compact
82
89
  rescue => e
83
90
  # We've seen some weird non-reproducible failures here
84
91
  @logger.error(
85
92
  'Something went wrong. Please please report this output.'
86
93
  )
87
94
  @logger.error(e)
88
- s.stdout.lines.each do |line|
95
+ stdout.lines.each do |line|
89
96
  @logger.error(line.strip)
90
97
  end
91
98
  exit(1)
@@ -93,16 +100,7 @@ module BetweenMeals
93
100
  end
94
101
 
95
102
  def update
96
- cmd = Mixlib::ShellOut.new(
97
- "#{@bin} pull --rebase", :cwd => File.expand_path(@repo_path)
98
- )
99
- cmd.run_command
100
- if cmd.exitstatus != 0
101
- @logger.error('Something went wrong with git!')
102
- @logger.error(cmd.stdout)
103
- fail
104
- end
105
- cmd.stdout
103
+ @cmd.pull.stdout
106
104
  end
107
105
 
108
106
  # Return all files
@@ -110,33 +108,35 @@ module BetweenMeals
110
108
  @repo.index.map { |x| { :path => x[:path], :status => :created } }
111
109
  end
112
110
 
113
- def status
114
- cmd = Mixlib::ShellOut.new(
115
- "#{@bin} status --porcelain 2>&1",
116
- :cwd => File.expand_path(@repo_path)
117
- )
118
- cmd.run_command
119
- if cmd.exitstatus != 0
120
- @logger.error('Something went wrong with git!')
121
- @logger.error(cmd.stdout)
122
- fail
111
+ def upstream?(rev, master = 'remotes/trunk')
112
+ if @cmd.merge_base(rev, master).stdout.strip == rev
113
+ return true
123
114
  end
124
- cmd.stdout
115
+ return false
116
+ rescue
117
+ return false
125
118
  end
126
119
 
127
- private
120
+ def status
121
+ @cmd.status.stdout.strip
122
+ end
123
+
124
+ def name
125
+ @cmd.config('user.name').stdout.strip
126
+ end
127
+
128
+ def email
129
+ @cmd.config('user.email').stdout.strip
130
+ end
128
131
 
129
- def check_refs(start_ref, end_ref)
130
- unless @repo.exists?(start_ref)
132
+ def valid_ref?(ref)
133
+ unless @repo.exists?(ref)
131
134
  fail Changeset::ReferenceError
132
135
  end
133
- unless end_ref.nil?
134
- unless @repo.exists?(end_ref)
135
- fail Changeset::ReferenceError
136
- end
137
- end
138
136
  end
139
137
 
138
+ private
139
+
140
140
  def parse_status(changes)
141
141
  # man git-diff-files
142
142
  # Possible status letters are:
@@ -0,0 +1,49 @@
1
+ # vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
2
+
3
+ # Copyright 2013-present Facebook
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'between_meals/cmd'
18
+
19
+ module BetweenMeals
20
+ class Repo
21
+ class Git < BetweenMeals::Repo
22
+ class Cmd < BetweenMeals::Cmd
23
+ def config(key)
24
+ cmd("config #{key}")
25
+ end
26
+
27
+ def clone(url, repo_path)
28
+ cmd("clone #{url} #{repo_path}", '/tmp')
29
+ end
30
+
31
+ def diff(start_ref, end_ref)
32
+ cmd("diff --name-status #{start_ref} #{end_ref}")
33
+ end
34
+
35
+ def pull
36
+ cmd('pull --rebase')
37
+ end
38
+
39
+ def merge_base(rev, master)
40
+ cmd("merge-base #{rev} #{master}")
41
+ end
42
+
43
+ def status
44
+ cmd('status --porcelain 2>&1')
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,214 @@
1
+ # vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
2
+
3
+ # Copyright 2013-present Facebook
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'pathname'
18
+ require 'mixlib/shellout'
19
+ require 'between_meals/changeset'
20
+ require 'between_meals/repo/hg/cmd'
21
+
22
+ module BetweenMeals
23
+ class Repo
24
+ class Hg < BetweenMeals::Repo
25
+ def setup
26
+ @bin = 'hg'
27
+ @cmd = BetweenMeals::Repo::Hg::Cmd.new(
28
+ :bin => @bin,
29
+ :cwd => @repo_path,
30
+ :logger => @logger,
31
+ )
32
+ end
33
+
34
+ def exists?
35
+ Dir.exists?(Pathname.new(@repo_path).join('.hg'))
36
+ end
37
+
38
+ def head_rev
39
+ @cmd.log('node').stdout
40
+ end
41
+
42
+ def checkout(url)
43
+ @cmd.clone(url, @repo_path)
44
+ end
45
+
46
+ # Return files changed between two revisions
47
+ def changes(start_ref, end_ref)
48
+ valid_ref?(start_ref)
49
+ valid_ref?(end_ref) if end_ref
50
+ stdout = @cmd.status(start_ref, end_ref).stdout
51
+ begin
52
+ parse_status(stdout).compact
53
+ rescue => e
54
+ # We've seen some weird non-reproducible failures here
55
+ @logger.error(
56
+ 'Something went wrong. Please please report this output.'
57
+ )
58
+ @logger.error(e)
59
+ stdout.lines.each do |line|
60
+ @logger.error(line.strip)
61
+ end
62
+ exit(1)
63
+ end
64
+ end
65
+
66
+ def update
67
+ @cmd.pull.stdout
68
+ rescue
69
+ @logger.error('Something went wrong with hg!')
70
+ @logger.error(cmd.stdout)
71
+ raise
72
+ end
73
+
74
+ # Return all files
75
+ def files
76
+ @cmd.manifest.stdout.split("\n").map do |x|
77
+ { :path => x, :status => :created }
78
+ end
79
+ end
80
+
81
+ def head_parents
82
+ [{
83
+ :time => Time.parse(@cmd.log('date|isodate', 'master').stdout),
84
+ :rev => @cmd.log('node', 'master').stdout,
85
+ }]
86
+ rescue
87
+ [{
88
+ :time => nil,
89
+ :rev => nil,
90
+ }]
91
+ end
92
+
93
+ def last_author
94
+ [
95
+ /^.*<(.*)>$/,
96
+ /^(.*@.*)$/,
97
+ ].each do |re|
98
+ m = @cmd.log('author').stdout.match(re)
99
+ return { :email => m[1] } if m
100
+ end
101
+ return { :email => nil }
102
+ end
103
+
104
+ def last_msg
105
+ @cmd.log('desc').stdout
106
+ rescue
107
+ nil
108
+ end
109
+
110
+ def last_msg=(msg)
111
+ if last_msg.strip != msg.strip
112
+ @cmd.amend(msg.strip)
113
+ end
114
+ end
115
+
116
+ def email
117
+ username[2]
118
+ rescue
119
+ nil
120
+ end
121
+
122
+ def name
123
+ username[1]
124
+ rescue
125
+ nil
126
+ end
127
+
128
+ def status
129
+ @cmd.status.stdout
130
+ end
131
+
132
+ def upstream?(rev)
133
+ # Check if commit is an ancestor of master
134
+ # Returns the diff if common ancestor is found,
135
+ # returns nothing if not
136
+ if @cmd.rev("'ancestor(master,#{rev}) & #{rev}'").stdout.empty?
137
+ return false
138
+ else
139
+ return true
140
+ end
141
+ end
142
+
143
+ def valid_ref?(ref)
144
+ @cmd.rev(ref)
145
+ return true
146
+ rescue
147
+ raise Changeset::ReferenceError
148
+ end
149
+
150
+ private
151
+
152
+ def username
153
+ @cmd.username.stdout.lines.first.strip.match(/^(.*?)(?:\s<(.*)>)?$/)
154
+ end
155
+
156
+ def parse_status(changes)
157
+ # The codes used to show the status of files are:
158
+ #
159
+ # M = modified
160
+ # A = added
161
+ # R = removed
162
+ # C = clean
163
+ # ! = missing (deleted by non-hg command, but still tracked)
164
+ # ? = not tracked
165
+ # I = ignored
166
+ # = origin of the previous file (with --copies)
167
+
168
+ # rubocop:disable MultilineBlockChain
169
+ changes.lines.map do |line|
170
+ case line
171
+ when /^A (\S+)$/
172
+ {
173
+ :status => :added,
174
+ :path => Regexp.last_match(1)
175
+ }
176
+ when /^C (\S+)$/
177
+ {
178
+ :status => :clean,
179
+ :path => Regexp.last_match(1)
180
+ }
181
+ when /^R (\S+)$/
182
+ {
183
+ :status => :deleted,
184
+ :path => Regexp.last_match(1)
185
+ }
186
+ when /^M (\S+)$/
187
+ {
188
+ :status => :modified,
189
+ :path => Regexp.last_match(1)
190
+ }
191
+ when /^! (\S+)$/
192
+ {
193
+ :status => :missing,
194
+ :path => Regexp.last_match(1)
195
+ }
196
+ when /^\? (\S+)$/
197
+ {
198
+ :status => :untracked,
199
+ :path => Regexp.last_match(1)
200
+ }
201
+ when /^I (\S+)$/
202
+ {
203
+ :status => :ignored,
204
+ :path => Regexp.last_match(1)
205
+ }
206
+ else
207
+ fail 'No match'
208
+ end
209
+ end
210
+ # rubocop:enable MultilineBlockChain
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,72 @@
1
+ # vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
2
+
3
+ # Copyright 2013-present Facebook
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'between_meals/cmd'
18
+ require 'tempfile'
19
+
20
+ module BetweenMeals
21
+ class Repo
22
+ class Hg < BetweenMeals::Repo
23
+ class Cmd < BetweenMeals::Cmd
24
+ def rev(rev)
25
+ cmd("log -r #{rev}")
26
+ end
27
+
28
+ def log(template, rev = '.')
29
+ cmd("log -r #{rev} -l 1 -T '{#{template}}'")
30
+ end
31
+
32
+ def clone(url, repo_path)
33
+ cmd("clone #{url} #{repo_path}")
34
+ end
35
+
36
+ def pull
37
+ cmd('pull --rebase')
38
+ end
39
+
40
+ def manifest
41
+ cmd('manifest')
42
+ end
43
+
44
+ def username
45
+ cmd('config ui.username')
46
+ end
47
+
48
+ def amend(msg)
49
+ f = Tempfile.new('between_meals.hg.amend')
50
+ begin
51
+ f.write(msg)
52
+ f.flush
53
+ cmd("commit --amend -l #{f.path}")
54
+ ensure
55
+ f.close
56
+ f.unlink
57
+ end
58
+ end
59
+
60
+ def status(start_ref = nil, end_ref = nil)
61
+ if start_ref && end_ref
62
+ cmd("status --rev #{start_ref} --rev #{end_ref}")
63
+ elsif start_ref
64
+ cmd("status --rev #{start_ref}")
65
+ else
66
+ cmd('status')
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end