honkster-braid 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
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