homesick 0.8.1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -0
- data/ChangeLog.markdown +3 -0
- data/README.markdown +78 -0
- data/Rakefile +1 -1
- data/homesick.gemspec +2 -2
- data/lib/homesick.rb +131 -12
- data/lib/homesick/actions.rb +1 -0
- data/spec/homesick_spec.rb +113 -1
- data/spec/spec_helper.rb +8 -1
- metadata +3 -3
data/.travis.yml
CHANGED
data/ChangeLog.markdown
CHANGED
data/README.markdown
CHANGED
@@ -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.
|
25
|
+
gem.version = "0.9.0"
|
26
26
|
gem.license = "MIT"
|
27
27
|
# Have dependencies? Add them to Gemfile
|
28
28
|
|
data/homesick.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "homesick"
|
8
|
-
s.version = "0.
|
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-
|
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"]
|
data/lib/homesick.rb
CHANGED
@@ -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
|
-
|
100
|
-
files.each do |path|
|
101
|
-
absolute_path = path.expand_path
|
100
|
+
subdirs = subdirs(name)
|
102
101
|
|
103
|
-
|
104
|
-
|
102
|
+
# link files
|
103
|
+
symlink_each(name, castle_dir(name), subdirs)
|
105
104
|
|
106
|
-
|
107
|
-
|
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
|
-
|
120
|
-
|
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 =
|
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
|
data/lib/homesick/actions.rb
CHANGED
data/spec/homesick_spec.rb
CHANGED
@@ -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('
|
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
|
data/spec/spec_helper.rb
CHANGED
@@ -16,11 +16,18 @@ RSpec.configure do |config|
|
|
16
16
|
homesick.stub(:say_status)
|
17
17
|
end
|
18
18
|
|
19
|
-
def given_castle(
|
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.
|
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-
|
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: -
|
174
|
+
hash: -1508111784299001289
|
175
175
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
176
176
|
none: false
|
177
177
|
requirements:
|