git-patch-patch 0.0.1
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.
- data/lib/git-patch-patch.rb +5 -0
- data/lib/git-patch-patch/patch_patcher.rb +225 -0
- data/lib/git-patch-patch/version.rb +9 -0
- metadata +112 -0
@@ -0,0 +1,225 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# from stdlib
|
4
|
+
require 'pathname'
|
5
|
+
require 'forwardable'
|
6
|
+
|
7
|
+
# from me
|
8
|
+
require 'git-trifle'
|
9
|
+
require 'path-accessor'
|
10
|
+
|
11
|
+
# this class knows how make a diff and
|
12
|
+
# write it to a retrievable patch
|
13
|
+
# how to apply a patch and commit it
|
14
|
+
module Git
|
15
|
+
|
16
|
+
class Trifle
|
17
|
+
|
18
|
+
# not too bright though
|
19
|
+
class PatchPatcher
|
20
|
+
|
21
|
+
# clean API best bud
|
22
|
+
extend Forwardable
|
23
|
+
|
24
|
+
# what we'll use from git-trifle
|
25
|
+
def_delegators :@t, :commits, :clone, :checkout
|
26
|
+
|
27
|
+
# current patch generated by the last diff performed
|
28
|
+
attr_reader :patch
|
29
|
+
|
30
|
+
# open the repo in path
|
31
|
+
# set the root path from where the patches are retrieved
|
32
|
+
def initialize(options)
|
33
|
+
options = {branch: 'master', patch_dir: '/tmp'}.merge options
|
34
|
+
|
35
|
+
# git handler
|
36
|
+
@t = Git::Trifle.new options[:repo]
|
37
|
+
# where the patch file will be stored
|
38
|
+
@patch_dir = Pathstring.new options[:patch_dir]
|
39
|
+
# branch from which the commits will be reviewed
|
40
|
+
@branch = options[:branch]
|
41
|
+
# list of reviewed commits
|
42
|
+
@patcher_commits = commits branch: @branch
|
43
|
+
end
|
44
|
+
|
45
|
+
def patch_work(pattern, replacement, *options, &block)
|
46
|
+
# work branch
|
47
|
+
checkout_work_branch
|
48
|
+
|
49
|
+
# main workflow
|
50
|
+
@patcher_commits.each_with_index do |c, id|
|
51
|
+
# exit on the last commit
|
52
|
+
break unless c_next = @patcher_commits[id + 1]
|
53
|
+
|
54
|
+
# current diff sets the current patch
|
55
|
+
diff c, c_next
|
56
|
+
|
57
|
+
# even if work is done we yield the patch
|
58
|
+
# to allow modification
|
59
|
+
patch.work == :done &&
|
60
|
+
yield(patch)
|
61
|
+
|
62
|
+
# if a patch file is found, we commit and
|
63
|
+
# jump to next iteration
|
64
|
+
patch.file.exist? &&
|
65
|
+
commit_patch(&block) &&
|
66
|
+
next
|
67
|
+
|
68
|
+
# patch filenames and / or patch content
|
69
|
+
options.each do |work|
|
70
|
+
patch.send "patch_#{work}", pattern, replacement
|
71
|
+
yield patch
|
72
|
+
end
|
73
|
+
|
74
|
+
# save to file and commit
|
75
|
+
patch.save
|
76
|
+
commit_patch &block
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# this class base operation
|
81
|
+
# a diff between two sha's stored in @patch
|
82
|
+
def diff(first_commit, second_commit)
|
83
|
+
@patch = PatchPatch.new @t.diff(first_commit, second_commit),
|
84
|
+
first_commit: first_commit,
|
85
|
+
second_commit: second_commit,
|
86
|
+
file: patch_file_for(second_commit),
|
87
|
+
work: patch_file_for(second_commit).exist?
|
88
|
+
end
|
89
|
+
|
90
|
+
# plain as plain :
|
91
|
+
# - patch from file
|
92
|
+
# - apply and commit with a reuse of the commit message
|
93
|
+
# from the second sha of the diff which generated the patch
|
94
|
+
def commit_patch
|
95
|
+
patch.error = nil
|
96
|
+
|
97
|
+
@t.apply patch.file
|
98
|
+
@t.add '.'
|
99
|
+
@t.commit '', reuse_message: patch.second_commit
|
100
|
+
rescue => error
|
101
|
+
# Houston we have a problem
|
102
|
+
patch.error = error
|
103
|
+
# first we yield to allow a fix
|
104
|
+
yield patch if block_given?
|
105
|
+
|
106
|
+
# if a fix was made, retry
|
107
|
+
if patch.changed?
|
108
|
+
patch.save
|
109
|
+
retry
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def checkout_work_branch
|
114
|
+
# checkout -b barbaric_name sha
|
115
|
+
checkout "__patch_patcher_#{Time.now.to_f}", commit: @patcher_commits.first
|
116
|
+
end
|
117
|
+
|
118
|
+
# unique patch filename for a given sha and a given local repo'
|
119
|
+
def patch_file_for(commit)
|
120
|
+
@patch_dir.join 'git-patch-patch',
|
121
|
+
# horrendous but necessary to ensure unicity
|
122
|
+
# of path, without having an absolute path
|
123
|
+
# that 'join' doesn't like
|
124
|
+
Pathname(@t.directory).realpath.to_s.sub('/',''),
|
125
|
+
commit.to_s,
|
126
|
+
'patch'
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
# patch model as a String for its primary
|
132
|
+
# interface, but it knows a lot more
|
133
|
+
class PatchPatch < String
|
134
|
+
|
135
|
+
# file handling
|
136
|
+
extend PathAccessor
|
137
|
+
|
138
|
+
path_accessor :file
|
139
|
+
attr_accessor :first_commit, :second_commit, :work, :error
|
140
|
+
|
141
|
+
# quick list of Strings methods that perform in-place
|
142
|
+
# modification
|
143
|
+
# we install a simple shunt on these methods to implement
|
144
|
+
# the changed? method
|
145
|
+
|
146
|
+
# :!= is left here by design to have changed? answer true
|
147
|
+
# the first time it is called after a possible change
|
148
|
+
instance_methods.grep(/\!/).push(:<<).each do |meth|
|
149
|
+
define_method meth do |*args, &block|
|
150
|
+
# record previous state
|
151
|
+
@previous = self.to_s
|
152
|
+
# call to vanilla
|
153
|
+
super *args, &block
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# defines patch_patch and patch_filenames
|
158
|
+
%w|filenames patch|.each do |work|
|
159
|
+
define_method "patch_#{work}".to_sym do |pattern, replacement|
|
160
|
+
# the actual job on patch
|
161
|
+
send work.to_sym, pattern, replacement
|
162
|
+
|
163
|
+
# changed to avoid messing with changed? the user
|
164
|
+
# could call
|
165
|
+
changed = self.to_s != @previous.to_s
|
166
|
+
@work = changed ? work.to_sym : :nothing
|
167
|
+
changed # return value says if it did anything
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def initialize(string, attributes=nil)
|
172
|
+
# instance attributes in one line
|
173
|
+
(attributes ||= {}).each { |k, v| send "#{k}=", v }
|
174
|
+
|
175
|
+
# job done ? ok then...
|
176
|
+
if @work == true || @work == :done
|
177
|
+
# give work the value we want
|
178
|
+
@work = :done
|
179
|
+
string = file.read
|
180
|
+
end
|
181
|
+
|
182
|
+
# changes handler
|
183
|
+
@previous = string.to_s
|
184
|
+
|
185
|
+
# vanilla
|
186
|
+
super string
|
187
|
+
end
|
188
|
+
|
189
|
+
def changed?
|
190
|
+
self != @previous
|
191
|
+
end
|
192
|
+
|
193
|
+
# to file
|
194
|
+
def save
|
195
|
+
# trailing \n or git complains
|
196
|
+
file.save!(self + "\n")
|
197
|
+
end
|
198
|
+
|
199
|
+
# patch content from file
|
200
|
+
def reload_from_file
|
201
|
+
@previous = self.to_s
|
202
|
+
replace file.read
|
203
|
+
end
|
204
|
+
|
205
|
+
private
|
206
|
+
|
207
|
+
# naughty nasty operation on filenames in patch
|
208
|
+
def filenames(pattern, replacement)
|
209
|
+
# we trap what begins with a:/ or b/ until we find the pattern
|
210
|
+
# it's replaced by what's captured with replacement appended
|
211
|
+
gsub! /(a|b)\/([^\n]*?)#{pattern}/, "\\1/\\2#{replacement}"
|
212
|
+
end
|
213
|
+
|
214
|
+
# Now this not pretty : we allow ourselves to alter anything
|
215
|
+
# in the patch according to pattern/replacement couple.
|
216
|
+
# Welcome conflictorama
|
217
|
+
def patch(pattern, replacement)
|
218
|
+
gsub! pattern, replacement
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: git-patch-patch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- lacravate
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: getopt
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: git-trifle
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: path-accessor
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: A script to rewrite git patches/commits, while keeping commits history
|
79
|
+
email:
|
80
|
+
- lacravate@lacravate.fr
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- lib/git-patch-patch.rb
|
86
|
+
- lib/git-patch-patch/patch_patcher.rb
|
87
|
+
- lib/git-patch-patch/version.rb
|
88
|
+
homepage: https://github.com/lacravate/git-patch-patch
|
89
|
+
licenses: []
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options: []
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project: ! '[none]'
|
108
|
+
rubygems_version: 1.8.24
|
109
|
+
signing_key:
|
110
|
+
specification_version: 3
|
111
|
+
summary: A script to rewrite git patches/commits, while keeping commits history
|
112
|
+
test_files: []
|