homesick 0.8.1 → 0.9.0
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/.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:
|