andsens / homeshick

git dotfiles synchronizer written in bash
MIT License
2.11k stars 145 forks source link

Your package destroyed all of my dotfiles #194

Closed therecluse26 closed 5 years ago

therecluse26 commented 5 years ago

When I followed the simple instructions you provided, your package rewrote most of my dotfiles with self-referential symbolic links, effectively deleting all of my configs. I don't know if it's recoverable, but I just thought you should either fix this or take the repo down to prevent this from happening to others (or explain to me how I can fix this issue).

Basically, a lot of my files are symlinking into the .homesick repo, and then are symbolic linking to themselves within the repo, so it's basically a pointer to nothing that overwrote all of them.

andsens commented 5 years ago

I am so sorry this happened to you.

In order to diagnose this issue properly I will need some more information to go on:

therecluse26 commented 5 years ago

Hi @andsens , thanks for the quick response

1) I ran the standard homeshick commands outlined in the tutorial, so after setting up the castle, homeshick track .dir_name and then homeshick cd dotfiles and the standard git add, commit, etc commands (I didn't push to remote yet)

2) It looked like a normal home directory. Any file that I didn't track is totally fine, it's just the dotfiles that I tracked with homeshick that this is happening to (and all of the files contained within)

3) Ok, so for example, this is in my home directory (the symlinks are red):

lrwxrwxrwx  1 brad brad   40 Sep  4 10:38  .directory -> .homesick/repos/dotfiles/home/.directory
lrwxrwxrwx  1 brad brad   35 Sep  4 10:38  .dmrc -> .homesick/repos/dotfiles/home/.dmrc

and in my .homesick/repos/dotfiles/home folder:

lrwxrwxrwx  1 brad brad   10 Sep  4 10:38 .directory -> .directory
lrwxrwxrwx  1 brad brad    5 Sep  4 10:38 .dmrc -> .dmrc
andsens commented 5 years ago

This is very puzzling. So, unless you said "yes" to an overwrite prompt at some point all your files are still there. Could you run find $HOME -name "somefile" where somefile is a file you know should be in .directory? (depending on the size of your homedir this might take a while).

Also: homeshick doesn't replace directories with symlinks, so the only explanation for why .directory in your home is now a symlink is because it was a symlink to begin with. Did you have some symlinking magic going on before using homeshick?

Which commit is homeshick on? I just released a new version 2 days ago where I fixed some quoting issues, hopefully it's 6cca371d. Then we have one less variable to consider.

therecluse26 commented 5 years ago

No, these files weren't symlinked prior (this overwrote literally every file that I had tracked with homeshick, I'm talking thousands and thousands of files) and there were no overwrite prompts given at any point.

I did what you requested with find and here's what got (and then trying to cat the files):

brad@brad-p1:~$ sudo find $HOME -name redshift.conf
/home/brad/.homesick/repos/dotfiles/home/.config/redshift.conf
/home/brad/.config/redshift.conf
brad@brad-p1:~$ cat /home/brad/.config/redshift.conf
cat: /home/brad/.config/redshift.conf: Too many levels of symbolic links
brad@brad-p1:~$ cat /home/brad/.homesick/repos/dotfiles/home/.config/redshift.conf
cat: /home/brad/.homesick/repos/dotfiles/home/.config/redshift.conf: Too many levels of symbolic links

This is the same with any other file I try. I think I need to just nuke and pave my home folder, unfortunately and reinstall my system.

andsens commented 5 years ago

Something is definitely missing here, homeshick track does not overwrite files at any point. The two potentially destructive actions in that command are mv -f "$homepath" "$repopath" which is guarded by a check for whether $repopath exists, and rm "$homepath" which is guarded by a check for whether $homepath is a symlink.

The best way for me to figure this out is if you did a search for homeshick in your $HISTFILE and copied the entire session into a gist (removing any commands with sensitive info), that way I can try and reproduce your steps myself and see where it may potentially have gone wrong.

therecluse26 commented 5 years ago

Ok @andsens , here's the Gist https://gist.github.com/therecluse26/bc7a74262f086411c2ac897579a282b3

I did run it a couple of times because it errored and I had to add several things to the .gitignore to get it tracking what I wanted.

therecluse26 commented 5 years ago

Also worth noting, I'm running Ubuntu 19.04

andsens commented 5 years ago

Great, I'll take a look at it once I get home tonight

thetrompf commented 5 years ago

I'm a bit curious how you ended up here, I having trouble reproducing this with the steps you provided, I have tried the following:

~ $ mkdir .dir_name
~ $ homeshick track .dir_name
Adds a file to a castle.
This moves the file into the castle and creates a symlink in its place.
Usage:
  homeshick track CASTLE FILE..

Missing the CASTLE arguement.

If I then tries to run it again providing dotfiles as CASTLE:

~ $ homeshick track dotfiles .dir_name
        track No files to track

However if I place a file inside the .dir_name directory the following happens:

 ~ $ touch .dir_name/testfile          
 ~ $ homeshick track dotfiles .dir_name
        track .dir_name/testfile

Homeshick uses git under the hood, and git does not track directories, however if you're trying to track a directory via the homeshick command line, it will:

  1. recusively find all files inside the directory. https://github.com/andsens/homeshick/blob/6cca371d6e/lib/commands/track.sh#L17-L22
  2. replicate the subdirectory structure of the directory you are trying to "track" inside the home directory of the castle. https://github.com/andsens/homeshick/blob/6cca371d6e/lib/commands/track.sh#L59
  3. move the actual files inside the castle. https://github.com/andsens/homeshick/blob/6cca371d6e/lib/commands/track.sh#L63-L88
  4. create symlinks back into your home directory. https://github.com/andsens/homeshick/blob/6cca371d6e/lib/commands/track.sh#L92

I was able to reproduce a scenario have the outcome you describe by doing the following:

1 .Enter interactive test environment:

user@host:~/.homesick/repos/homeshick/test/interactive bash
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
  1. Generate test castle
    user@host:~$ homeshick generate castle
     generate castle
  2. Create test dot-file and track it
    user@host:~$ touch .file
    user@host:~$ homeshick track castle .file
        track .file
  3. Verify everything is as expected
    user@host:~$ homeshick cd castle/home
    user@host:~/.homesick/repos/castle/home$ ls -lah
    total 8,0K
    drwxr-xr-x 2 user user 4,0K sep  5 12:44 .
    drwxr-xr-x 4 user user 4,0K sep  5 12:44 ..
    -rw-r--r-- 1 user user    0 sep  5 12:44 .file      <- real file
    user@host:~/.homesick/repos/castle/home$ cd
    user@host:~$ ls -lah
    total 28K
    drwxr-xr-x 4 user user 4,0K sep  5 12:44 .
    drwx------ 5 user user 4,0K sep  5 12:44 ..
    -rw-r--r-- 1 user user  152 sep  5 12:44 .bashrc
    drwxr-xr-x 3 user user 4,0K sep  5 12:44 .config
    -rw-r--r-- 1 user user   71 sep  5 12:44 .cshrc
    lrwxrwxrwx 1 user user   33 sep  5 12:44 .file -> .homesick/repos/castle/home/.file    <- correctly symlinked
    drwxr-xr-x 3 user user 4,0K sep  5 12:44 .homesick
    -rw-r--r-- 1 user user  143 sep  5 12:44 .zshrc
    user@host:~$ rm -Rf .homesick/repos/castle
  4. Create a dead symlink into the repository (it can be done by either rm -Rf /path/to/castle or git reset --hard or git checkout or similar that leaving the symlink dead).
    user@host:~$ rm -Rf .homesick/repos/castle
    user@host:~$ homeshick generate castle
     generate castle
  5. If you then try to "retrack" the file, the thing you experienced occurs:
    
    user@host:~$ homeshick track castle .file
        track .file

user@host:~$ homeshick cd castle/home user@host:~/.homesick/repos/castle/home$ ls -lah total 8,0K drwxr-xr-x 2 user user 4,0K sep 5 12:32 . drwxr-xr-x 4 user user 4,0K sep 5 12:32 .. lrwxrwxrwx 1 user user 5 sep 5 12:32 .file -> .file


<summary>command list reproducing with `rm -Rf`</summary>
<details>

homeshick generate castle touch .file homeshick track castle .file homeshick cd castle/home ls -lah cd ls -lah rm -Rf .homesick/repos/castle homeshick generate castle homeshick track castle .file homeshick cd castle/home ls -lah


</details>

<summary>command list reproducing with `git reset --hard`</summary>
<details>

homeshick generate castle touch .file homeshick track castle .file homeshick cd castle git reset --hard mkdir home cd homeshick track castle .file homeshick cd castle/home ls -lah



</details>
andsens commented 5 years ago

I have to agree with @thetrompf here. The two reproduces seem like a very likely explanation of what happened. I can see you used git reset --hard origin/master in your history, could it be that you did that when homeshick track had already move the files into the castle? Because that would definitely nuke your data.

therecluse26 commented 5 years ago

@andsens Yeah, that must be what happened. Maybe the solution would be to have a check before creating the symlinks to see if they'll be self-referential and then throwing either an error or just bypassing them?

andsens commented 5 years ago

@therecluse26 yeah that could work. Remember though, the files are already gone at this point. So it's not like having this check would prevent any data loss. On the other hand, allowing homeshick to link into repositories can facilitate some use-cases where you have one castle that patches another castle. homeshick allows for some pretty weird setups exactly because there are very few assumptions coded into the tool itself.
In the end it would just be more code to maintain, test, and debug to little avail.

I added a little warning to the tutorial to make new users aware that modifications to the repo will directly affect their dotfiles

therecluse26 commented 5 years ago

@andsens No worries, I already rebuilt my configs anyways, just figured it could be helpful to add a check that prevents this from happening to others in case they do something dumb like I did.

Thanks for all of your hard work and promptness