amp-git 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.document +5 -0
  2. data/.gitignore +23 -0
  3. data/Gemfile +15 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +17 -0
  6. data/Rakefile +68 -0
  7. data/VERSION +1 -0
  8. data/features/amp-git.feature +9 -0
  9. data/features/step_definitions/amp-git_steps.rb +0 -0
  10. data/features/support/env.rb +4 -0
  11. data/lib/amp-git/encoding/binary_delta.rb +171 -0
  12. data/lib/amp-git/repo_format/changeset.rb +348 -0
  13. data/lib/amp-git/repo_format/commit_object.rb +87 -0
  14. data/lib/amp-git/repo_format/index.rb +169 -0
  15. data/lib/amp-git/repo_format/loose_object.rb +78 -0
  16. data/lib/amp-git/repo_format/packfile.rb +263 -0
  17. data/lib/amp-git/repo_format/packfile_index.rb +196 -0
  18. data/lib/amp-git/repo_format/raw_object.rb +56 -0
  19. data/lib/amp-git/repo_format/staging_area.rb +215 -0
  20. data/lib/amp-git/repo_format/tag_object.rb +87 -0
  21. data/lib/amp-git/repo_format/tree_object.rb +98 -0
  22. data/lib/amp-git/repo_format/versioned_file.rb +133 -0
  23. data/lib/amp-git/repositories/local_repository.rb +192 -0
  24. data/lib/amp-git/repository.rb +57 -0
  25. data/lib/amp-git.rb +49 -0
  26. data/lib/amp_plugin.rb +1 -0
  27. data/spec/amp-git_spec.rb +15 -0
  28. data/spec/repository_spec.rb +74 -0
  29. data/spec/spec.opts +1 -0
  30. data/spec/spec_helper.rb +29 -0
  31. data/test/index_tests/index +0 -0
  32. data/test/index_tests/test_helper.rb +16 -0
  33. data/test/index_tests/test_index.rb +69 -0
  34. data/test/packfile_tests/hasindex.idx +0 -0
  35. data/test/packfile_tests/hasindex.pack +0 -0
  36. data/test/packfile_tests/pack-4e1941122fd346526b0a3eee2d92f3277a0092cd.pack +0 -0
  37. data/test/packfile_tests/pack-d23ff2538f970371144ae7182c28730b11eb37c1.idx +0 -0
  38. data/test/packfile_tests/test_helper.rb +16 -0
  39. data/test/packfile_tests/test_packfile.rb +75 -0
  40. data/test/packfile_tests/test_packfile_index_v2.rb +90 -0
  41. data/test/packfile_tests/test_packfile_with_index.rb +76 -0
  42. data/test/test_commit_object.rb +60 -0
  43. data/test/test_git_delta.rb +67 -0
  44. data/test/test_helper.rb +71 -0
  45. data/test/test_loose_object.rb +51 -0
  46. data/test/test_tag_object.rb +72 -0
  47. data/test/test_tree_object.rb +55 -0
  48. metadata +215 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ .hg*
23
+ *.gemspec
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ # This is an install-only file - it's not used by the application
2
+ source "http://rubygems.org"
3
+ source "http://gems.github.com"
4
+
5
+ gem 'amp-front', '>= 0.1.0'
6
+ gem 'amp-core', '>= 0.1.0'
7
+
8
+ group :development do
9
+ gem 'jeweler'
10
+ gem 'rspec', '< 2.0.0'
11
+ gem 'yard'
12
+ gem 'cucumber'
13
+ gem 'devver-construct', :require => 'construct'
14
+ gem 'minitest'
15
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Michael Edgar
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,17 @@
1
+ = amp-git
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2010 Michael Edgar. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,68 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = 'amp-git'
8
+ gem.summary = %Q{The git plugin for Amp.}
9
+ gem.description = %Q{This gem provides git support to Amp. It is bundled with Amp itself.}
10
+ gem.email = 'michael.j.edgar@dartmouth.edu'
11
+ gem.homepage = 'http://github.com/michaeledgar/amp-git'
12
+ gem.authors = ['Michael Edgar']
13
+ gem.add_dependency 'amp-front', '>= 0.1.0'
14
+ gem.add_dependency 'amp-core', '>= 0.1.0'
15
+ gem.add_development_dependency 'rspec', '>= 1.2.9'
16
+ gem.add_development_dependency 'yard', '>= 0'
17
+ gem.add_development_dependency 'cucumber', '>= 0'
18
+ gem.add_development_dependency 'minitest', '>= 1.7'
19
+ gem.add_development_dependency 'devver-construct', '>= 1.1.0'
20
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
21
+ end
22
+ Jeweler::GemcutterTasks.new
23
+ rescue LoadError
24
+ puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
25
+ end
26
+
27
+ require 'spec/rake/spectask'
28
+ Spec::Rake::SpecTask.new(:spec) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.spec_files = FileList['spec/**/*_spec.rb']
31
+ end
32
+
33
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
34
+ spec.libs << 'lib' << 'spec'
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ require 'rake/testtask'
40
+ Rake::TestTask.new do |t|
41
+ t.libs << 'test'
42
+ t.test_files = FileList['test/**/test*.rb']
43
+ t.verbose = true
44
+ end
45
+
46
+ task :spec => :check_dependencies
47
+
48
+ begin
49
+ require 'cucumber/rake/task'
50
+ Cucumber::Rake::Task.new(:features)
51
+
52
+ task :features => :check_dependencies
53
+ rescue LoadError
54
+ task :features do
55
+ abort 'Cucumber is not available. In order to run features, you must: sudo gem install cucumber'
56
+ end
57
+ end
58
+
59
+ task :default => [:spec, :test]
60
+
61
+ begin
62
+ require 'yard'
63
+ YARD::Rake::YardocTask.new
64
+ rescue LoadError
65
+ task :yardoc do
66
+ abort 'YARD is not available. In order to run yardoc, you must: sudo gem install yard'
67
+ end
68
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,9 @@
1
+ Feature: something something
2
+ In order to something something
3
+ A user something something
4
+ something something something
5
+
6
+ Scenario: something something
7
+ Given inspiration
8
+ When I create a sweet new gem
9
+ Then everyone should see how awesome I am
File without changes
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
2
+ require 'amp-git'
3
+
4
+ require 'spec/expectations'
@@ -0,0 +1,171 @@
1
+ ##################################################################
2
+ # Licensing Information #
3
+ # #
4
+ # The following code is licensed, as standalone code, under #
5
+ # the Ruby License, unless otherwise directed within the code. #
6
+ # #
7
+ # For information on the license of this code when distributed #
8
+ # with and used in conjunction with the other modules in the #
9
+ # Amp project, please see the root-level LICENSE file. #
10
+ # #
11
+ # © Michael J. Edgar and Ari Brown, 2009-2010 #
12
+ # #
13
+ ##################################################################
14
+
15
+ # This was written by reading the Git Book. No source code was
16
+ # examined to produce this code. It is the original work of its
17
+ # creators, Michael Edgar and Ari Brown.
18
+ #
19
+ # http://git.rsbx.net/Documents/Git_Data_Formats.txt
20
+
21
+ module Amp
22
+ module Core
23
+ module Repositories
24
+ module Git
25
+
26
+ ##
27
+ # All of git's specific encoding classes.
28
+ module Encoding
29
+ class DeltaError < StandardError; end
30
+
31
+ ##
32
+ # Handles the binary delta format that are found in Git's packfiles.
33
+ # The format is detailed in the link above.
34
+ class BinaryDelta
35
+ attr_accessor :base_length, :result_length, :hunks
36
+
37
+ ##
38
+ # Parses a git-format binary delta from a string.
39
+ #
40
+ # @param [String] delta the delta to parse
41
+ def initialize(delta)
42
+ fp = StringIO.new(delta)
43
+ @base_length = read_little_endian_base128(fp)
44
+ @result_length = read_little_endian_base128(fp)
45
+ @hunks = []
46
+ while !fp.eof?
47
+ @hunks << Hunk.parse(fp)
48
+ end
49
+ end
50
+
51
+ ##
52
+ # Applies the binary delta to an original text. Returns the patched
53
+ # data.
54
+ #
55
+ # @param [String] original the text to patch with the delta
56
+ # @return the patched data
57
+ def apply(original)
58
+ if original.size != @base_length
59
+ raise DeltaError.new("Expected input data to be #{@base_length} bytes, but was #{original.size} bytes.")
60
+ end
61
+ output = StringIO.new
62
+ @hunks.each do |hunk|
63
+ hunk.apply(output, original)
64
+ end
65
+ if output.string.size != @result_length
66
+ raise DeltaError.new("Expected patched data to be #{@result_length} bytes, but was #{output.string.size} bytes.")
67
+ end
68
+ output.string
69
+ end
70
+
71
+ private
72
+
73
+ ##
74
+ # Reads a little endian, base-128 value from a stream.
75
+ # This is a variable-length integer, where bytes 0 to N-1 have an MSB
76
+ # of 1, and byte N has an MSB of 0.
77
+ #
78
+ # @param [IO, #read] fp the input stream to read the value from
79
+ # @return [Integer] the encoded integer
80
+ def read_little_endian_base128(fp)
81
+ result = shift = 0
82
+ begin
83
+ byte = Support::StringUtils.ord(fp.read(1))
84
+ result |= (byte & 0x7f) << shift
85
+ shift += 7
86
+ end while byte & 0x80 > 0
87
+ result
88
+ end
89
+
90
+ ##
91
+ # @api private
92
+ class Hunk
93
+ ##
94
+ # Parses a hunk from the input stream. Each hunk is an action: either a
95
+ # a copy from an input stream, or an "insert" which inserts specified
96
+ # data.
97
+ def self.parse(fp)
98
+ opcode = Support::StringUtils.ord(fp.read(1))
99
+ if opcode & 0x80 == 0
100
+ InsertHunk.new(opcode, fp)
101
+ else
102
+ CopyHunk.new(opcode, fp)
103
+ end
104
+ end
105
+ end
106
+
107
+ ##
108
+ # A Hunk that performs an insert operation. One of two types of delta
109
+ # hunks in the git binary delta format.
110
+ class InsertHunk
111
+ ##
112
+ # Creates a new insert hunk.
113
+ #
114
+ # @param [Fixnum] opcode the opcode that identifies this hunk's properties.
115
+ # @param [IO, #read] fp the input stream we're reading this delta from
116
+ def initialize(opcode, fp)
117
+ @data = fp.read(opcode & 0x7f)
118
+ end
119
+
120
+ ##
121
+ # Applies the Hunk with a given output buffer with an input string.
122
+ #
123
+ # @param [IO, #write] output the output buffer we're building up
124
+ # @param [String] input the original data. ignored for InsertHunk.
125
+ def apply(output, input)
126
+ output.write @data
127
+ end
128
+ end
129
+
130
+ ##
131
+ # A Hunk that performs a copy operation. One of two types of delta
132
+ # hunks in the git binary delta format.
133
+ class CopyHunk
134
+ ##
135
+ # Creates a new copy hunk.
136
+ #
137
+ # @param [Fixnum] opcode the opcode that identifies this hunk's properties.
138
+ # @param [IO, #read] fp the input stream we're reading this delta from
139
+ def initialize(opcode, fp)
140
+ @offset = @length = 0
141
+ shift = 0
142
+ 0.upto(3) do
143
+ @offset |= Support::StringUtils.ord(fp.read(1)) << shift if opcode & 0x01 > 0
144
+ opcode >>= 1
145
+ shift += 8
146
+ end
147
+ shift = 0
148
+ 0.upto(2) do
149
+ @length |= Support::StringUtils.ord(fp.read(1)) << shift if opcode & 0x01 > 0
150
+ opcode >>= 1
151
+ shift += 8
152
+ end
153
+ @length = 1 << 16 if @length == 0
154
+ end
155
+
156
+ ##
157
+ # Applies the Hunk with a given output buffer with an input string.
158
+ #
159
+ # @param [IO, #write] output the output buffer we're building up
160
+ # @param [String] input the original data. Used for copies.
161
+ def apply(output, input)
162
+ output.write input[@offset...@offset + @length]
163
+ end
164
+ end
165
+
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,348 @@
1
+ ##################################################################
2
+ # Licensing Information #
3
+ # #
4
+ # The following code is licensed, as standalone code, under #
5
+ # the Ruby License, unless otherwise directed within the code. #
6
+ # #
7
+ # For information on the license of this code when distributed #
8
+ # with and used in conjunction with the other modules in the #
9
+ # Amp project, please see the root-level LICENSE file. #
10
+ # #
11
+ # © Michael J. Edgar and Ari Brown, 2009-2010 #
12
+ # #
13
+ ##################################################################
14
+
15
+ require 'time'
16
+
17
+ module Amp
18
+ module Git
19
+
20
+ ##
21
+ # A Changeset is a simple way of accessing the repository within a certain
22
+ # revision. For example, if the user specifies revision # 36, or revision
23
+ # 3adf21, then we can look those up, and work within the repository at the
24
+ # moment of that revision.
25
+ class Changeset < Amp::Core::Repositories::AbstractChangeset
26
+
27
+ attr_accessor :repo
28
+ attr_reader :revision
29
+ alias_method :repository, :repo
30
+
31
+ def initialize(repo, rev)
32
+ @repo = repo
33
+ if short_name.kind_of?(Integer)
34
+ @revision = rev
35
+ @node_id = convert_rev_to_node(rev)
36
+ else
37
+ @revision = convert_node_to_rev(rev)
38
+ @node_id = rev
39
+ end
40
+ end
41
+
42
+ def node; @node_id; end
43
+
44
+
45
+ ##
46
+ # Compares 2 changesets so we can sort them and whatnot
47
+ #
48
+ # @param [Changeset] other a changeset we will compare against
49
+ # @return [Integer] -1, 0, or 1. Typical comparison.
50
+ def <=>(other)
51
+ date <=> other.date
52
+ end
53
+
54
+ ##
55
+ # Iterates over every tracked file at this point in time.
56
+ #
57
+ # @return [Changeset] self, because that's how #each works
58
+ def each(&b)
59
+ all_files.each( &b)
60
+ self
61
+ end
62
+
63
+ ##
64
+ # the nodes that this node inherits from
65
+ #
66
+ # @return [Array<Abstract Changeset>]
67
+ def parents
68
+ parse!
69
+ @parents
70
+ end
71
+
72
+ ##
73
+ # Retrieve +filename+
74
+ #
75
+ # @return [AbstractVersionedFile]
76
+ def get_file(filename)
77
+ VersionedFile.new @repo, file, :revision => node
78
+ end
79
+ alias_method :[], :get_file
80
+
81
+ ##
82
+ # When was the changeset made?
83
+ #
84
+ # @return [Time]
85
+ def date
86
+ parse!
87
+ @date
88
+ end
89
+
90
+ ##
91
+ # The user who made the changeset
92
+ #
93
+ # @return [String] the user who made the changeset
94
+ def user
95
+ parse!
96
+ @user
97
+ end
98
+
99
+ ##
100
+ # Which branch this changeset belongs to
101
+ #
102
+ # @return [String] the user who made the changeset
103
+ def branch
104
+ parse!
105
+ raise NotImplementedError.new("branch() must be implemented by subclasses of AbstractChangeset.")
106
+ end
107
+
108
+ ##
109
+ # @return [String]
110
+ def description
111
+ parse!
112
+ @description
113
+ end
114
+
115
+ ##
116
+ # What files have been altered in this changeset?
117
+ #
118
+ # @return [Array<String>]
119
+ def altered_files
120
+ parse!
121
+ @altered_files
122
+ end
123
+
124
+ ##
125
+ # Returns a list of all files that are tracked at this current revision.
126
+ #
127
+ # @return [Array<String>] the files tracked at the given revision
128
+ def all_files
129
+ parse!
130
+ @all_files
131
+ end
132
+
133
+ # Is this changeset a working changeset?
134
+ #
135
+ # @return [Boolean] is the changeset representing the working directory?
136
+ def working?
137
+ false
138
+ end
139
+
140
+ private
141
+
142
+
143
+ ##
144
+ # Converts a semi-reliable revision # into a git changeset node.
145
+ def convert_rev_to_node(rev)
146
+ %x(git rev-list --reverse HEAD).split("\n")[rev - 1]
147
+ end
148
+
149
+ ##
150
+ # Converts a git changeset node into a semi-reliable revision #
151
+ def convert_node_to_rev(node)
152
+ %x(git rev-list --reverse HEAD | grep -n #{node} | cut -d: -f1).to_i
153
+ end
154
+
155
+
156
+ # yeah, i know, you could combine these all into one for a clean sweep.
157
+ # but it's clearer this way
158
+ def parse!
159
+ return if @parsed
160
+
161
+ # the parents
162
+ log_data = `git log -1 #{node}^ 2> /dev/null`
163
+
164
+ # DETERMINING PARENTS
165
+ dad = log_data[/^commit (.+)$/, 1]
166
+ dad = dad ? dad[0..6] : nil
167
+ mom = nil
168
+
169
+ if log_data =~ /^Merge: (.+)\.\.\. (.+)\.\.\.$/ # Merge: 1c002dd... 35cfb2b...
170
+ dad = $1 # just have them both use the short name, nbd
171
+ mom = $2
172
+ end
173
+
174
+ @parents = [dad, mom].compact.map {|r| Changeset.new repo, r }
175
+
176
+ # the actual changeset
177
+ log_data = `git log -1 #{node} 2> /dev/null`
178
+
179
+ # DETERMINING DATE
180
+ @date = Time.parse log_data[/^Date:\s+(.+)$/, 1]
181
+
182
+ # DETERMINING USER
183
+ @user = log_data[/^Author:\s+(.+)$/, 1]
184
+
185
+ # DETERMINING DESCRIPTION
186
+ @description = log_data.split("\n")[4..-1].map {|l| l.strip }.join "\n"
187
+
188
+ # ALTERED FILES
189
+ @altered_files = `git log -1 #{node} --pretty=oneline --name-only 2> /dev/null`.split("\n")[1..-1]
190
+
191
+ # ALL FILES
192
+ # @all_files is also sorted. Hooray!
193
+ @all_files = `git ls-tree -r #{node}`.split("\n").map do |line|
194
+ # 100644 blob cdbeb2a42b714a4db49293c87fec4e180d07d44f .autotest
195
+ line[/^\d+ \w+ \w+\s+(.+)$/, 1]
196
+ end
197
+
198
+ @parsed = true
199
+ end
200
+
201
+ end
202
+
203
+ class WorkingDirectoryChangeset < Amp::Repositories::AbstractChangeset
204
+
205
+ attr_accessor :repo
206
+ alias_method :repository, :repo
207
+
208
+ def initialize(repo, opts={:text => ''})
209
+ @repo = repo
210
+ @text = opts[:text]
211
+ @date = Time.parse opts[:date].to_s
212
+ @user = opts[:user]
213
+ @parents = opts[:parents].map {|p| Changeset.new(@repo, p) } if opts[:parents]
214
+ @status = opts[:changes]
215
+ end
216
+
217
+ ##
218
+ # the nodes that this node inherits from
219
+ #
220
+ # @return [Array<Abstract Changeset>]
221
+ def parents
222
+ @parents || (parse! && @parents)
223
+ end
224
+
225
+ def revision; nil; end
226
+
227
+ ##
228
+ # Retrieve +filename+
229
+ #
230
+ # @return [AbstractVersionedFile]
231
+ def get_file(filename)
232
+ VersionedWorkingFile.new @repo, filename
233
+ end
234
+ alias_method :[], :get_file
235
+
236
+ ##
237
+ # When was the changeset made?
238
+ #
239
+ # @return [Time]
240
+ def date
241
+ Time.now
242
+ end
243
+
244
+ ##
245
+ # The user who made the changeset
246
+ #
247
+ # @return [String] the user who made the changeset
248
+ def user
249
+ @user ||= @repo.config.username
250
+ end
251
+
252
+ ##
253
+ # Which branch this changeset belongs to
254
+ #
255
+ # @return [String] the user who made the changeset
256
+ def branch
257
+ @branch ||= `git branch 2> /dev/null`[/\*\s(.+)$/, 1]
258
+ end
259
+
260
+ ##
261
+ # @return [String]
262
+ def description
263
+ @text || ''
264
+ end
265
+
266
+ def status
267
+ @status ||= @repo.status :unknown => true
268
+ end
269
+
270
+ ##
271
+ # Iterates over every tracked file at this point in time.
272
+ #
273
+ # @return [Changeset] self, because that's how #each works
274
+ def each(&b)
275
+ all_files.each( &b)
276
+ self
277
+ end
278
+
279
+ ##
280
+ # Returns a list of all files that are tracked at this current revision.
281
+ #
282
+ # @return [Array<String>] the files tracked at the given revision
283
+ def all_files
284
+ @all_files ||= `git ls-files 2> /dev/null`.split("\n")
285
+ end
286
+
287
+ # Is this changeset a working changeset?
288
+ #
289
+ # @return [Boolean] is the changeset representing the working directory?
290
+ def working?
291
+ true
292
+ end
293
+
294
+ ##
295
+ # Recursively walk the directory tree, getting all files that +match+ says
296
+ # are good.
297
+ #
298
+ # @param [Amp::Match] match how to select the files in the tree
299
+ # @param [Boolean] check_ignored (false) should we check for ignored files?
300
+ # @return [Array<String>] an array of filenames in the tree that match +match+
301
+ def walk(match, check_ignored = false)
302
+ tree = @repo.staging_area.walk true, check_ignored, match
303
+ tree.keys.sort
304
+ end
305
+
306
+ # What files have been altered in this changeset?
307
+ def altered_files; `git show --name-only #{node} 2> /dev/null`.split("\n"); end
308
+ # What files have changed?
309
+ def modified; status[:modified]; end
310
+ # What files have we added?
311
+ def added; status[:added]; end
312
+ # What files have been removed?
313
+ def removed; status[:removed]; end
314
+ # What files have been deleted (but not officially)?
315
+ def deleted; status[:deleted]; end
316
+ # What files are hanging out, but untracked?
317
+ def unknown; status[:unknown]; end
318
+ # What files are pristine since the last revision?
319
+ def clean; status[:normal]; end
320
+
321
+ # yeah, i know, you could combine these all into one for a clean sweep.
322
+ # but it's clearer this way
323
+ def parse!
324
+ return if @parsed
325
+
326
+ log_data = `git log -1 HEAD 2> /dev/null`
327
+
328
+ unless log_data.empty?
329
+ # DETERMINING PARENTS
330
+ commit = log_data[/^commit (.+)$/, 1]
331
+ dad = commit ? commit[0..6] : nil
332
+ mom = nil
333
+
334
+ if log_data =~ /^Merge: (.+)\.\.\. (.+)\.\.\.$/ # Merge: 1c002dd... 35cfb2b...
335
+ dad = $1 # just have them both use the short name, nbd
336
+ mom = $2
337
+ end
338
+
339
+ @parents = [dad, mom].compact.map {|p| Changeset.new @repo, p }
340
+ else
341
+ @parents = []
342
+ end
343
+ @parsed = true
344
+ end
345
+
346
+ end
347
+ end
348
+ end