honkster-braid 0.6.2

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/bin/braid ADDED
@@ -0,0 +1,243 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
4
+ require 'braid'
5
+
6
+ require 'rubygems'
7
+ require 'main'
8
+
9
+ Home = File.expand_path(ENV['HOME'] || '~')
10
+
11
+ # mostly blantantly stolen from ara's punch script
12
+ # main kicks ass!
13
+ Main {
14
+ description <<-TXT
15
+ braid is a simple tool to help track git or svn repositories inside a git repository.
16
+
17
+ Run 'braid commandname help' for more details.
18
+
19
+ All operations will be executed in the braid/track branch.
20
+ You can then merge back or cherry-pick changes.
21
+ TXT
22
+
23
+ mode(:add) {
24
+ description <<-TXT
25
+ Add a new mirror to be tracked.
26
+
27
+ * adds metadata about the mirror to .braids
28
+ * adds the git or git svn remotes to .git/config
29
+ * fetches and merges remote code into given directory
30
+
31
+ --type defaults:
32
+
33
+ * svn://path # => svn
34
+ * git://path # => git
35
+ * http://path/trunk # => svn
36
+ * http://path.git # => git
37
+
38
+ Name defaults:
39
+
40
+ * remote/path # => path
41
+ * remote/path/trunk # => path
42
+ * remote/path.git # => path
43
+ TXT
44
+
45
+ examples <<-TXT
46
+ . braid add svn://remote/path
47
+ . braid add svn://remote/path local/dir
48
+ . braid add git://remote/path local/dir
49
+ . braid add http://remote/path.git local/dir
50
+ . braid add http://remote/path --type git local/dir
51
+ . braid add svn://remote/path --branch notmaster
52
+ TXT
53
+
54
+ mixin :argument_url, :option_type, :optional_path, :option_branch, :option_rails_plugin, :option_revision, :option_full, :option_verbose
55
+
56
+ run {
57
+ Braid.verbose = verbose
58
+ Braid::Command.run(:add, url, { "type" => type, "path" => path, "branch" => branch, "rails_plugin" => rails_plugin, "revision" => revision, "full" => full })
59
+ }
60
+ }
61
+
62
+ mode(:update) {
63
+ description <<-TXT
64
+ Update a braid mirror.
65
+
66
+ * get new changes from remote
67
+ * always creates a merge commit
68
+ * updates metadata in .braids when revisions are changed
69
+
70
+ Defaults to updating all unlocked mirrors if none is specified.
71
+ TXT
72
+
73
+ examples <<-TXT
74
+ . braid update
75
+ . braid update local/dir
76
+ TXT
77
+
78
+ mixin :optional_path, :option_revision, :option_head, :option_verbose
79
+
80
+ run {
81
+ Braid.verbose = verbose
82
+ Braid::Command.run(:update, path, { "revision" => revision, "head" => head })
83
+ }
84
+ }
85
+
86
+ mode(:remove) {
87
+ description <<-TXT
88
+ Remove a mirror.
89
+
90
+ * removes metadata from .braids
91
+ * removes the local directory and commits the removal
92
+ * removes the git remote by default, --keep can be used to supress that
93
+ TXT
94
+
95
+ examples <<-TXT
96
+ . braid remove local/dir
97
+ TXT
98
+
99
+ mixin :argument_path, :option_verbose, :option_keep_remote
100
+
101
+ run {
102
+ options = {
103
+ :keep => keep
104
+ }
105
+ Braid.verbose = verbose
106
+ Braid::Command.run(:remove, path, options)
107
+ }
108
+ }
109
+
110
+ mode(:diff) {
111
+ description <<-TXT
112
+ Show diff of local changes to mirror.
113
+ TXT
114
+
115
+ mixin :argument_path, :option_verbose
116
+
117
+ run {
118
+ Braid::Command.run(:diff, path)
119
+ }
120
+ }
121
+
122
+ mode(:push) {
123
+ description <<-TXT
124
+ Push local mirror changes to remote.
125
+ TXT
126
+
127
+ mixin :argument_path, :option_verbose
128
+
129
+ run {
130
+ Braid.verbose = verbose
131
+ Braid::Command.run(:push, path)
132
+ }
133
+ }
134
+
135
+ mode(:setup) {
136
+ description <<-TXT
137
+ Set up git and git-svn remotes.
138
+ TXT
139
+
140
+ mixin :optional_path, :option_verbose
141
+
142
+ run {
143
+ Braid.verbose = verbose
144
+ Braid::Command.run(:setup, path)
145
+ }
146
+ }
147
+
148
+ mode(:version) {
149
+ description 'Show braid version.'
150
+
151
+ run {
152
+ puts "braid #{Braid::VERSION}"
153
+ }
154
+ }
155
+
156
+ mixin(:argument_path) {
157
+ argument(:path) {
158
+ attr
159
+ }
160
+ }
161
+
162
+ mixin(:optional_path) {
163
+ argument(:path) {
164
+ optional
165
+ attr
166
+ }
167
+ }
168
+
169
+ mixin(:argument_url) {
170
+ argument(:url) {
171
+ attr
172
+ }
173
+ }
174
+
175
+ mixin(:option_type) {
176
+ option(:type, :t) {
177
+ optional
178
+ argument :required
179
+ desc 'mirror type'
180
+ attr
181
+ }
182
+ }
183
+
184
+ mixin(:option_branch) {
185
+ option(:branch, :b) {
186
+ optional
187
+ argument :required
188
+ desc 'remote branch name'
189
+ attr
190
+ }
191
+ }
192
+
193
+ mixin(:option_rails_plugin) {
194
+ option(:rails_plugin, :p) {
195
+ optional
196
+ desc 'added mirror is a Rails plugin'
197
+ attr
198
+ }
199
+ }
200
+
201
+ mixin(:option_revision) {
202
+ option(:revision, :r) {
203
+ optional
204
+ argument :required
205
+ desc 'revision to track'
206
+ attr
207
+ }
208
+ }
209
+
210
+ mixin(:option_head) {
211
+ option(:head) {
212
+ optional
213
+ desc 'mirror head'
214
+ attr
215
+ }
216
+ }
217
+
218
+ mixin(:option_full) {
219
+ option(:full) {
220
+ optional
221
+ desc 'include mirror history' # FIXME
222
+ attr
223
+ }
224
+ }
225
+
226
+ mixin(:option_verbose) {
227
+ option(:verbose, :v) {
228
+ optional
229
+ desc 'log shell commands'
230
+ attr
231
+ }
232
+ }
233
+
234
+ mixin(:option_keep_remote) {
235
+ option(:keep) {
236
+ optional
237
+ desc 'do not remove the remote'
238
+ attr
239
+ }
240
+ }
241
+
242
+ run { help! }
243
+ }
data/braid.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %Q{#{ENV["GEM_PREFIX"] ? ENV["GEM_PREFIX"] : ""}braid}
3
+ s.version = "0.6.2"
4
+
5
+ s.specification_version = 2 if s.respond_to? :specification_version=
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Cristi Balan", "Norbert Crombach"]
9
+ s.date = %q{2008-10-29}
10
+ s.default_executable = %q{braid}
11
+ s.description = %q{A simple tool for tracking vendor branches in git.}
12
+ s.email = %q{evil@che.lu}
13
+ s.executables = ["braid"]
14
+ s.files = Dir["bin/*"] + ["braid.gemspec"] + Dir["lib/**/*.rb"] + Dir["test/**/*.rb"]
15
+ s.has_rdoc = false
16
+ s.homepage = %q{http://evil.che.lu/projects/braid}
17
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "braid", "--main"]
18
+ s.require_paths = ["lib"]
19
+ s.rubyforge_project = %q{braid}
20
+ s.rubygems_version = %q{1.1.0}
21
+ s.summary = %q{A simple tool for tracking vendor branches in git.}
22
+
23
+ s.add_dependency(%q<main>, [">= 3.0.0"])
24
+ s.add_dependency(%q<open4>, [">= 0.9.6"])
25
+ end
data/lib/braid.rb ADDED
@@ -0,0 +1,30 @@
1
+ $:.unshift dirname = File.dirname(__FILE__)
2
+
3
+ module Braid
4
+ VERSION = "0.6.1"
5
+
6
+ CONFIG_FILE = ".braids"
7
+ REQUIRED_GIT_VERSION = "1.6"
8
+
9
+ def self.verbose; @verbose || false; end
10
+ def self.verbose=(new_value); @verbose = !!new_value; end
11
+
12
+ def self.use_local_cache; [nil, "true", "1"].include?(ENV["BRAID_USE_LOCAL_CACHE"]); end
13
+ def self.local_cache_dir; File.expand_path(ENV["BRAID_LOCAL_CACHE_DIR"] || "#{ENV["HOME"]}/.braid/cache"); end
14
+
15
+ class BraidError < StandardError
16
+ def message
17
+ value = super
18
+ value if value != self.class.name
19
+ end
20
+ end
21
+ end
22
+
23
+ require dirname + '/core_ext'
24
+ require 'braid/operations'
25
+ require 'braid/mirror'
26
+ require 'braid/config'
27
+ require 'braid/command'
28
+ Dir[dirname + '/braid/commands/*'].each do |file|
29
+ require file
30
+ end
@@ -0,0 +1,131 @@
1
+ module Braid
2
+ class Command
3
+ class InvalidRevision < BraidError
4
+ end
5
+
6
+ extend Operations::VersionControl
7
+ include Operations::VersionControl
8
+
9
+ def self.run(command, *args)
10
+ verify_git_version!
11
+
12
+ klass = Commands.const_get(command.to_s.capitalize)
13
+ klass.new.run(*args)
14
+
15
+ rescue BraidError => error
16
+ case error
17
+ when Operations::ShellExecutionError
18
+ msg "Shell error: #{error.message}"
19
+ else
20
+ msg "Error: #{error.message}"
21
+ end
22
+ exit(1)
23
+ end
24
+
25
+ def self.msg(str)
26
+ puts "Braid: #{str}"
27
+ end
28
+
29
+ def msg(str)
30
+ self.class.msg(str)
31
+ end
32
+
33
+ def config
34
+ @config ||= load_and_migrate_config
35
+ end
36
+
37
+ def verbose?
38
+ Braid.verbose
39
+ end
40
+
41
+ private
42
+ def setup_remote(mirror)
43
+ Command.run(:setup, mirror.path)
44
+ end
45
+
46
+ def use_local_cache?
47
+ Braid.use_local_cache
48
+ end
49
+
50
+ def self.verify_git_version!
51
+ git.require_version!(REQUIRED_GIT_VERSION)
52
+ end
53
+
54
+ def bail_on_local_changes!
55
+ git.ensure_clean!
56
+ end
57
+
58
+ def with_reset_on_error
59
+ work_head = git.head
60
+
61
+ begin
62
+ yield
63
+ rescue => error
64
+ msg "Resetting to '#{work_head[0, 7]}'."
65
+ git.reset_hard(work_head)
66
+ raise error
67
+ end
68
+ end
69
+
70
+ def load_and_migrate_config
71
+ config = Config.new
72
+ unless config.valid?
73
+ msg "Configuration is outdated. Migrating."
74
+ bail_on_local_changes!
75
+ config.migrate!
76
+ git.commit("Upgrade .braids", "-- .braids")
77
+ end
78
+ config
79
+ end
80
+
81
+ def add_config_file
82
+ git.add(CONFIG_FILE)
83
+ end
84
+
85
+ def display_revision(mirror, revision = nil)
86
+ revision ||= mirror.revision
87
+ mirror.type == "svn" ? "r#{revision}" : "'#{revision[0, 7]}'"
88
+ end
89
+
90
+ def validate_new_revision(mirror, new_revision)
91
+ unless new_revision
92
+ unless mirror.type == "svn"
93
+ return git.rev_parse(mirror.remote)
94
+ else
95
+ return svn.head_revision(mirror.url)
96
+ end
97
+ end
98
+
99
+ unless mirror.type == "svn"
100
+ new_revision = git.rev_parse(new_revision)
101
+ else
102
+ new_revision = svn.clean_revision(new_revision)
103
+ end
104
+ old_revision = mirror.revision
105
+
106
+ if new_revision == old_revision
107
+ raise InvalidRevision, "mirror is already at requested revision"
108
+ end
109
+
110
+ if mirror.type == "svn"
111
+ if old_revision && new_revision < old_revision
112
+ raise InvalidRevision, "local revision is higher than request revision"
113
+ end
114
+
115
+ if svn.head_revision(mirror.url) < new_revision
116
+ raise InvalidRevision, "requested revision is higher than remote revision"
117
+ end
118
+ end
119
+
120
+ new_revision
121
+ end
122
+
123
+ def determine_target_revision(mirror, new_revision)
124
+ unless mirror.type == "svn"
125
+ git.rev_parse(new_revision)
126
+ else
127
+ git_svn.commit_hash(mirror.remote, new_revision)
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,40 @@
1
+ module Braid
2
+ module Commands
3
+ class Add < Command
4
+ def run(url, options = {})
5
+ bail_on_local_changes!
6
+
7
+ with_reset_on_error do
8
+ mirror = config.add_from_options(url, options)
9
+
10
+ branch_message = (mirror.type == "svn" || mirror.branch == "master") ? "" : " branch '#{mirror.branch}'"
11
+ revision_message = options["revision"] ? " at #{display_revision(mirror, options["revision"])}" : ""
12
+ msg "Adding #{mirror.type} mirror of '#{mirror.url}'#{branch_message}#{revision_message}."
13
+
14
+ # these commands are explained in the subtree merge guide
15
+ # http://www.kernel.org/pub/software/scm/git/docs/howto/using-merge-subtree.html
16
+
17
+ setup_remote(mirror)
18
+ mirror.fetch
19
+
20
+ new_revision = validate_new_revision(mirror, options["revision"])
21
+ target_revision = determine_target_revision(mirror, new_revision)
22
+
23
+ unless mirror.squashed?
24
+ git.merge_ours(target_revision)
25
+ end
26
+ git.read_tree_prefix(target_revision, mirror.path)
27
+
28
+ mirror.revision = new_revision
29
+ mirror.lock = new_revision if options["revision"]
30
+ config.update(mirror)
31
+ add_config_file
32
+
33
+ git.commit("Add mirror '#{mirror.path}' at #{display_revision(mirror)}")
34
+ msg "Added mirror at #{display_revision(mirror)}."
35
+ end
36
+ end
37
+
38
+ end
39
+ end
40
+ end