homesick 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 2.0.0
3
4
  - 1.9.3
4
5
  - 1.8.7
@@ -1,3 +1,6 @@
1
+ # 0.9.0
2
+ * Introduce .homesick_subdir #39
3
+
1
4
  # 0.8.1
2
5
  *Fixed `homesick list` bug on ruby 2.0 #37
3
6
 
@@ -51,6 +51,84 @@ Not sure what else homesick has up its sleeve? There's always the built in help:
51
51
 
52
52
  homesick help
53
53
 
54
+ ## .homesick_subdir
55
+
56
+ `homesick symlink` basically makes symlink to only first depth in `castle/home`. If you want to link nested files/directories, please use .homesick_subdir.
57
+
58
+ For example, when you have castle like this:
59
+
60
+ castle/home
61
+ `-- .config
62
+ `-- fooapp
63
+ |-- config1
64
+ |-- config2
65
+ `-- config3
66
+
67
+ and have home like this:
68
+
69
+ $ tree -a
70
+ ~
71
+ |-- .config
72
+ | `-- barapp
73
+ | |-- config1
74
+ | |-- config2
75
+ | `-- config3
76
+ `-- .emacs.d
77
+ |-- elisp
78
+ `-- inits
79
+
80
+ You may want to symlink only to `castle/home/.config/fooapp` instead of `castle/home/.config` because you already have `~/.config/barapp`. In this case, you can use .homesick_subdir. Please write "directories you want to look up sub direcoties (instead of just first depth)" in this file.
81
+
82
+ castle/home/.homesick_subdir
83
+
84
+ .config
85
+
86
+ and run `homesick symlink CASTLE`. The result is:
87
+
88
+ ~
89
+ |-- .config
90
+ | |-- barapp
91
+ | | |-- config1
92
+ | | |-- config2
93
+ | | `-- config3
94
+ | `-- fooapp -> castle/home/.config/fooapp
95
+ `-- .emacs.d
96
+ |-- elisp
97
+ `-- inits
98
+
99
+ Or `homesick track NESTED_FILE CASTLE` adds a line automatically. For example:
100
+
101
+ homesick track .emacs.d/elisp castle
102
+
103
+ castle/home/.homesick_subdir
104
+
105
+ .config
106
+ .emacs.d
107
+
108
+ home directory
109
+
110
+ ~
111
+ |-- .config
112
+ | |-- barapp
113
+ | | |-- config1
114
+ | | |-- config2
115
+ | | `-- config3
116
+ | `-- fooapp -> castle/home/.config/fooapp
117
+ `-- .emacs.d
118
+ |-- elisp -> castle/home/.emacs.d/elisp
119
+ `-- inits
120
+
121
+ and castle
122
+
123
+ castle/home
124
+ |-- .config
125
+ | `-- fooapp
126
+ | |-- config1
127
+ | |-- config2
128
+ | `-- config3
129
+ `-- .emacs.d
130
+ `-- elisp
131
+
54
132
  ## Note on Patches/Pull Requests
55
133
 
56
134
  * Fork the project.
data/Rakefile CHANGED
@@ -22,7 +22,7 @@ Jeweler::Tasks.new do |gem|
22
22
  gem.email = ["josh@technicalpickles.com", "info@muratayusuke.com"]
23
23
  gem.homepage = "http://github.com/technicalpickles/homesick"
24
24
  gem.authors = ["Joshua Nichols", "Yusuke Murata"]
25
- gem.version = "0.8.1"
25
+ gem.version = "0.9.0"
26
26
  gem.license = "MIT"
27
27
  # Have dependencies? Add them to Gemfile
28
28
 
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "homesick"
8
- s.version = "0.8.1"
8
+ s.version = "0.9.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Joshua Nichols", "Yusuke Murata"]
12
- s.date = "2013-05-19"
12
+ s.date = "2013-06-06"
13
13
  s.description = "\n A man's home (directory) is his castle, so don't leave home with out it.\n\n Homesick is sorta like rip, but for dotfiles. It uses git to clone a repository containing dotfiles, and saves them in ~/.homesick. It then allows you to symlink all the dotfiles into place with a single command. \n\n "
14
14
  s.email = ["josh@technicalpickles.com", "info@muratayusuke.com"]
15
15
  s.executables = ["homesick"]
@@ -10,6 +10,7 @@ class Homesick < Thor
10
10
  add_runtime_options!
11
11
 
12
12
  GITHUB_NAME_REPO_PATTERN = /\A([A-Za-z_-]+\/[A-Za-z_-]+)\Z/
13
+ SUBDIR_FILENAME = ".homesick_subdir"
13
14
 
14
15
  def initialize(args=[], options={}, config={})
15
16
  super
@@ -96,15 +97,14 @@ class Homesick < Thor
96
97
  check_castle_existance(name, "symlink")
97
98
 
98
99
  inside castle_dir(name) do
99
- files = Pathname.glob('{.*,*}').reject{|a| [".",".."].include?(a.to_s)}
100
- files.each do |path|
101
- absolute_path = path.expand_path
100
+ subdirs = subdirs(name)
102
101
 
103
- inside home_dir do
104
- adjusted_path = (home_dir + path).basename
102
+ # link files
103
+ symlink_each(name, castle_dir(name), subdirs)
105
104
 
106
- ln_s absolute_path, adjusted_path
107
- end
105
+ # link files in subdirs
106
+ subdirs.each do |subdir|
107
+ symlink_each(name, subdir, subdirs)
108
108
  end
109
109
  end
110
110
  end
@@ -112,22 +112,46 @@ class Homesick < Thor
112
112
  desc "track FILE CASTLE", "add a file to a castle"
113
113
  def track(file, castle)
114
114
  castle = Pathname.new(castle)
115
- file = Pathname.new(file)
115
+ file = Pathname.new(file.chomp('/'))
116
116
  check_castle_existance(castle, 'track')
117
117
 
118
118
  absolute_path = file.expand_path
119
- castle_path = castle_dir(castle)
120
- mv absolute_path, castle_path
119
+ relative_dir = absolute_path.relative_path_from(home_dir).dirname
120
+ castle_path = Pathname.new(castle_dir(castle)).join(relative_dir)
121
+ FileUtils.mkdir_p castle_path
122
+
123
+ # Are we already tracking this or anything inside it?
124
+ target = Pathname.new(castle_path.join(file.basename))
125
+ if target.exist?
126
+ if absolute_path.directory?
127
+ move_dir_contents(target, absolute_path)
128
+ absolute_path.rmtree
129
+ subdir_remove(castle, relative_dir + file.basename)
130
+
131
+ elsif more_recent? absolute_path, target
132
+ target.delete
133
+ mv absolute_path, castle_path
134
+ else
135
+ shell.say_status(:track, "#{target} already exists, and is more recent than #{file}. Run 'homesick SYMLINK CASTLE' to create symlinks.", :blue) unless options[:quiet]
136
+ end
137
+ else
138
+ mv absolute_path, castle_path
139
+ end
121
140
 
122
141
  inside home_dir do
123
- absolute_path = castle_dir(castle) + file.basename
124
- home_path = home_dir + file
142
+ absolute_path = castle_path + file.basename
143
+ home_path = home_dir + relative_dir + file.basename
125
144
  ln_s absolute_path, home_path
126
145
  end
127
146
 
128
147
  inside castle_path do
129
148
  git_add absolute_path
130
149
  end
150
+
151
+ # are we tracking something nested? Add the parent dir to the manifest
152
+ unless relative_dir.eql?(Pathname.new('.'))
153
+ subdir_add(castle, relative_dir)
154
+ end
131
155
  end
132
156
 
133
157
  desc "list", "List cloned castles"
@@ -219,4 +243,99 @@ class Homesick < Thor
219
243
  git_push
220
244
  end
221
245
  end
246
+
247
+ def subdir_file(castle)
248
+ repos_dir.join(castle, SUBDIR_FILENAME)
249
+ end
250
+
251
+ def subdirs(castle)
252
+ subdir_filepath = subdir_file(castle)
253
+ subdirs = []
254
+ if subdir_filepath.exist?
255
+ subdir_filepath.readlines.each do |subdir|
256
+ subdirs.push(subdir.chomp)
257
+ end
258
+ end
259
+ subdirs
260
+ end
261
+
262
+ def subdir_add(castle, path)
263
+ subdir_filepath = subdir_file(castle)
264
+ File.open(subdir_filepath, 'a+') do |subdir|
265
+ subdir.puts path unless subdir.readlines.inject(false) { |memo, line| line.eql?("#{path.to_s}\n") || memo }
266
+ end
267
+
268
+ inside castle_dir(castle) do
269
+ git_add subdir_filepath
270
+ end
271
+ end
272
+
273
+ def subdir_remove(castle, path)
274
+ subdir_filepath = subdir_file(castle)
275
+ if subdir_filepath.exist?
276
+ lines = IO.readlines(subdir_filepath).delete_if { |line| line == "#{path}\n" }
277
+ File.open(subdir_filepath, 'w') { |manfile| manfile.puts lines }
278
+ end
279
+
280
+ inside castle_dir(castle) do
281
+ git_add subdir_filepath
282
+ end
283
+ end
284
+
285
+ def move_dir_contents(target, dir_path)
286
+ child_files = dir_path.children
287
+ child_files.each do |child|
288
+
289
+ target_path = target.join(child.basename)
290
+ if target_path.exist?
291
+ if more_recent?(child, target_path) && target.file?
292
+ target_path.delete
293
+ mv child, target
294
+ end
295
+ next
296
+ end
297
+
298
+ mv child, target
299
+ end
300
+ end
301
+
302
+ def more_recent?(first, second)
303
+ first_p = Pathname.new(first)
304
+ second_p = Pathname.new(second)
305
+ first_p.mtime > second_p.mtime && !first_p.symlink?
306
+ end
307
+
308
+ def symlink_each(castle, basedir, subdirs)
309
+ absolute_basedir = Pathname.new(basedir).expand_path
310
+ inside basedir do
311
+ files = Pathname.glob('{.*,*}').reject{|a| [".", ".."].include?(a.to_s)}
312
+ files.each do |path|
313
+ absolute_path = path.expand_path
314
+ castle_home = castle_dir(castle)
315
+
316
+ # make ignore dirs
317
+ ignore_dirs = []
318
+ subdirs.each do |subdir|
319
+ # ignore all parent of each line in subdir file
320
+ Pathname.new(subdir).ascend do |p|
321
+ ignore_dirs.push(p)
322
+ end
323
+ end
324
+
325
+ # ignore dirs written in subdir file
326
+ matched = false
327
+ ignore_dirs.uniq.each do |ignore_dir|
328
+ if absolute_path == castle_home.join(ignore_dir)
329
+ matched = true
330
+ break
331
+ end
332
+ end
333
+ next if matched
334
+
335
+ relative_dir = absolute_basedir.relative_path_from(castle_home)
336
+ home_path = home_dir.join(relative_dir).join(path)
337
+ ln_s absolute_path, home_path
338
+ end
339
+ end
340
+ end
222
341
  end
@@ -93,6 +93,7 @@ class Homesick
93
93
  def ln_s(source, destination, config = {})
94
94
  source = Pathname.new(source)
95
95
  destination = Pathname.new(destination)
96
+ FileUtils.mkdir_p destination.dirname
96
97
 
97
98
  if destination.symlink?
98
99
  if destination.readlink == source
@@ -120,12 +120,59 @@ describe "homesick" do
120
120
  existing_dotdir_link.readlink.should == dotdir
121
121
  end
122
122
  end
123
+
124
+ context "with '.config' in .homesick_subdir" do
125
+ let(:castle) { given_castle("glencairn", [".config"]) }
126
+ it "can symlink in sub directory" do
127
+ dotdir = castle.directory(".config")
128
+ dotfile = dotdir.file(".some_dotfile")
129
+
130
+ homesick.symlink("glencairn")
131
+
132
+ home_dotdir = home.join(".config")
133
+ home_dotdir.symlink?.should == false
134
+ home_dotdir.join(".some_dotfile").readlink.should == dotfile
135
+ end
136
+ end
137
+
138
+ context "with '.config/appA' in .homesick_subdir" do
139
+ let(:castle) { given_castle("glencairn", [".config/appA"]) }
140
+ it "can symlink in nested sub directory" do
141
+ dotdir = castle.directory(".config").directory("appA")
142
+ dotfile = dotdir.file(".some_dotfile")
143
+
144
+ homesick.symlink("glencairn")
145
+
146
+ home_dotdir = home.join(".config").join("appA")
147
+ home_dotdir.symlink?.should == false
148
+ home_dotdir.join(".some_dotfile").readlink.should == dotfile
149
+ end
150
+ end
151
+
152
+ context "with '.config' and '.config/appA' in .homesick_subdir" do
153
+ let(:castle) { given_castle("glencairn", [".config", ".config/appA"]) }
154
+ it "can symlink under both of .config and .config/appA" do
155
+ config_dir = castle.directory(".config")
156
+ config_dotfile = config_dir.file(".some_dotfile")
157
+ appA_dir = config_dir.directory("appA")
158
+ appA_dotfile = appA_dir.file(".some_appfile")
159
+
160
+ homesick.symlink("glencairn")
161
+
162
+ home_config_dir = home.join(".config")
163
+ home_appA_dir = home_config_dir.join("appA")
164
+ home_config_dir.symlink?.should == false
165
+ home_config_dir.join(".some_dotfile").readlink.should == config_dotfile
166
+ home_appA_dir.symlink?.should == false
167
+ home_appA_dir.join(".some_appfile").readlink.should == appA_dotfile
168
+ end
169
+ end
123
170
  end
124
171
 
125
172
  describe "list" do
126
173
  it "should say each castle in the castle directory" do
127
174
  given_castle('zomg')
128
- given_castle('zomg', 'wtf/zomg')
175
+ given_castle('wtf/zomg')
129
176
 
130
177
  homesick.should_receive(:say_status).with("zomg", "git://github.com/technicalpickles/zomg.git", :cyan)
131
178
  homesick.should_receive(:say_status).with("wtf/zomg", "git://github.com/technicalpickles/zomg.git", :cyan)
@@ -169,5 +216,70 @@ describe "homesick" do
169
216
 
170
217
  some_rc_file.readlink.should == tracked_file
171
218
  end
219
+
220
+ it 'should track a file in nested folder structure' do
221
+ castle = given_castle('castle_repo')
222
+
223
+ some_nested_file = home.file('some/nested/file.txt')
224
+ homesick.track(some_nested_file.to_s, 'castle_repo')
225
+
226
+ tracked_file = castle.join('some/nested/file.txt')
227
+ tracked_file.should exist
228
+ some_nested_file.readlink.should == tracked_file
229
+ end
230
+
231
+ it 'should track a nested directory' do
232
+ castle = given_castle('castle_repo')
233
+
234
+ some_nested_dir = home.directory('some/nested/directory/')
235
+ homesick.track(some_nested_dir.to_s, 'castle_repo')
236
+
237
+ tracked_file = castle.join('some/nested/directory/')
238
+ tracked_file.should exist
239
+ some_nested_dir.realpath.should == tracked_file.realpath
240
+ end
241
+
242
+ describe "subdir_file" do
243
+
244
+ it 'should add the nested files parent to the subdir_file' do
245
+ castle = given_castle('castle_repo')
246
+
247
+ some_nested_file = home.file('some/nested/file.txt')
248
+ homesick.track(some_nested_file.to_s, 'castle_repo')
249
+
250
+ subdir_file = castle.parent.join(Homesick::SUBDIR_FILENAME)
251
+ File.open(subdir_file, 'r') do |f|
252
+ f.readline.should == "some/nested\n"
253
+ end
254
+ end
255
+
256
+ it 'should NOT add anything if the files parent is already listed' do
257
+ castle = given_castle('castle_repo')
258
+
259
+ some_nested_file = home.file('some/nested/file.txt')
260
+ other_nested_file = home.file('some/nested/other.txt')
261
+ homesick.track(some_nested_file.to_s, 'castle_repo')
262
+ homesick.track(other_nested_file.to_s, 'castle_repo')
263
+
264
+ subdir_file = castle.parent.join(Homesick::SUBDIR_FILENAME)
265
+ File.open(subdir_file, 'r') do |f|
266
+ f.readlines.size.should == 1
267
+ end
268
+ end
269
+
270
+ it 'should remove the parent of a tracked file from the subdir_file if the parent itself is tracked' do
271
+ castle = given_castle('castle_repo')
272
+
273
+ some_nested_file = home.file('some/nested/file.txt')
274
+ nested_parent = home.directory('some/nested/')
275
+ homesick.track(some_nested_file.to_s, 'castle_repo')
276
+ homesick.track(nested_parent.to_s, 'castle_repo')
277
+
278
+ subdir_file = castle.parent.join(Homesick::SUBDIR_FILENAME)
279
+ File.open(subdir_file, 'r') do |f|
280
+ f.each_line { |line| line.should_not == "some/nested\n" }
281
+ end
282
+ end
283
+ end
172
284
  end
173
285
  end
@@ -16,11 +16,18 @@ RSpec.configure do |config|
16
16
  homesick.stub(:say_status)
17
17
  end
18
18
 
19
- def given_castle(name, path=name)
19
+ def given_castle(path, subdirs=[])
20
+ name = Pathname.new(path).basename
20
21
  castles.directory(path) do |castle|
21
22
  Dir.chdir(castle) do
22
23
  system "git init >/dev/null 2>&1"
23
24
  system "git remote add origin git://github.com/technicalpickles/#{name}.git >/dev/null 2>&1"
25
+ if subdirs then
26
+ subdir_file = castle.join(Homesick::SUBDIR_FILENAME)
27
+ subdirs.each do |subdir|
28
+ system "echo #{subdir} >> #{subdir_file}"
29
+ end
30
+ end
24
31
  return castle.directory("home")
25
32
  end
26
33
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: homesick
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-05-19 00:00:00.000000000 Z
13
+ date: 2013-06-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: thor
@@ -171,7 +171,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
171
171
  version: '0'
172
172
  segments:
173
173
  - 0
174
- hash: -3689253364441722964
174
+ hash: -1508111784299001289
175
175
  required_rubygems_version: !ruby/object:Gem::Requirement
176
176
  none: false
177
177
  requirements: