Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Carefully reinstall .git and .gitignore files #251

Merged
merged 1 commit into from
Mar 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ If you'd like to modify which files are backed up, you have to edit the `~/.conf
1. Select the appropriate option in the CLI and follow the prompts.
2. Open the file in a text editor and make your changes.

#### .gitignore

As of `v4.0`, any `.gitignore` changes should be made in the `shallow-backup` config file. `.gitignore` changes that are meant to apply to all directories should be under the `root-gitignore` key. Dotfile specific gitignores should be placed under the `dotfiles-gitignore` key. The original `default-gitignore` key in the config is still supported for backwards compatibility, however, converting to the new config format is strongly encouraged.

#### Output Structure
---

Expand Down
12 changes: 6 additions & 6 deletions shallow_backup/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,19 +95,19 @@ def cli(add_dot, all, configs, delete_config, destroy_backup, dotfiles, fonts, n
backup_home_path = expand_to_abs_path(get_config()["backup_path"])
mkdir_warn_overwrite(backup_home_path)
repo, new_git_repo_created = safe_git_init(backup_home_path)
create_gitignore(backup_home_path, "root-gitignore")

# Create default gitignore if we just ran git init
if new_git_repo_created:
safe_create_gitignore(backup_home_path)
# Prompt user for remote URL
if not remote:
git_url_prompt(repo)
# Prompt user for remote URL if needed
if new_git_repo_created and not remote:
git_url_prompt(repo)

# Set remote URL from CLI arg
if remote:
git_set_remote(repo, remote)

dotfiles_path = os.path.join(backup_home_path, "dotfiles")
create_gitignore(dotfiles_path, "dotfiles-gitignore")

configs_path = os.path.join(backup_home_path, "configs")
packages_path = os.path.join(backup_home_path, "packages")
fonts_path = os.path.join(backup_home_path, "fonts")
Expand Down
16 changes: 9 additions & 7 deletions shallow_backup/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,16 @@ def get_default_config():
".ssh",
".vim"
],
"default-gitignore": [
"root-gitignore": [
"dotfiles/.ssh",
"dotfiles/.pypirc",
".DS_Store"
],
"dotfiles-gitignore": [
".ssh",
".pypirc",
".DS_Store",
],
"config_mapping" : get_config_paths()
}

Expand Down Expand Up @@ -142,18 +147,15 @@ def show_config():
"""
print_section_header("SHALLOW BACKUP CONFIG", Fore.RED)
for section, contents in get_config().items():
# Hide gitignore config
if section == "default-gitignore":
continue
# Print backup path on same line
elif section == "backup_path":
if section == "backup_path":
print_path_red("Backup Path:", contents)
elif section == "config_mapping":
print_red_bold("Configs:")
print_red_bold("\nConfigs:")
for path, dest in contents.items():
print(" {} -> {}".format(path, dest))
# Print section header and intent contents. (Dotfiles/folders)
else:
print_red_bold("\n{}: ".format(section.capitalize()))
print_red_bold("\n{}: ".format(section.replace("-", " ").capitalize()))
for item in contents:
print(" {}".format(item))
2 changes: 1 addition & 1 deletion shallow_backup/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class ProjInfo:
PROJECT_NAME = 'shallow-backup'
VERSION = '3.4'
VERSION = '4.0'
AUTHOR_GITHUB = 'alichtman'
AUTHOR_FULL_NAME = 'Aaron Lichtman'
DESCRIPTION = "Easily create lightweight backups of installed packages, dotfiles, and more."
Expand Down
25 changes: 15 additions & 10 deletions shallow_backup/git_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,24 @@ def git_set_remote(repo, remote_url):
origin.fetch()


def safe_create_gitignore(dir_path):
def create_gitignore(dir_path, key):
"""
Creates a .gitignore file that ignores all files listed in config.
Handles backwards compatibility for the default-gitignore -> root-gitignore
change and the introduction of the dotfiles-gitignore key in v4.0.
"""
gitignore_path = os.path.join(dir_path, ".gitignore")
if os.path.exists(gitignore_path):
print_yellow_bold("Detected .gitignore file.")
else:
print_yellow_bold("Creating default .gitignore...")
files_to_ignore = get_config()["default-gitignore"]
with open(gitignore_path, "w+") as f:
for ignore in files_to_ignore:
f.write("{}\n".format(ignore))
print_yellow_bold(f"Updating .gitignore file at {gitignore_path} with config from {key}")
try:
files_to_ignore = get_config()[key]
except KeyError:
if key == "root-gitignore":
files_to_ignore = get_config()["default-gitignore"]
elif key == "dotfiles-gitignore":
pass
with open(os.path.join(dir_path, ".gitignore"), "w+") as f:
for ignore in files_to_ignore:
f.write("{}\n".format(ignore))


def safe_git_init(dir_path):
Expand Down Expand Up @@ -87,7 +92,7 @@ def git_add_all_commit_push(repo, message, separate_dotfiles_repo=False):
repo.git.add(A=True)
try:
repo.git.commit(m=COMMIT_MSG[message])
# Git submodule issue https://github.com/alichtman/shallow-backup/issues/229
# Git submodule issue https://github.com/alichtman/shallow-backup/issues/229
except git.exc.GitCommandError as e:
error = e.stdout.strip()
error = error[error.find("\'") + 1:-1]
Expand Down
11 changes: 8 additions & 3 deletions shallow_backup/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,19 @@ def destroy_backup_dir(backup_path):

def get_abs_path_subfiles(directory):
"""
Returns list of absolute paths of files and folders contained in a directory, excluding .git directories.
Returns list of absolute paths of files and folders contained in a directory,
excluding the root .git directory and root .gitignore..
"""
file_paths = []
for path, _, files in os.walk(directory):
for name in files:
joined = os.path.join(path, name)
if "/.git/" not in joined:
file_paths.append(os.path.join(path, name))
root_git_dir = os.path.join(directory, ".git")
root_gitignore = os.path.join(directory, ".gitignore")
if root_git_dir not in joined and root_gitignore not in joined:
file_paths.append(joined)
else:
print(f"Excluded: {joined}")
return file_paths


Expand Down
4 changes: 2 additions & 2 deletions tests/test_git_folder_moving.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import shutil
from .test_utils import BACKUP_DEST_DIR, FAKE_HOME_DIR, DIRS, setup_env_vars, create_config_for_test
sys.path.insert(0, "../shallow_backup")
from shallow_backup.git_wrapper import move_git_repo, safe_git_init, safe_create_gitignore
from shallow_backup.git_wrapper import move_git_repo, safe_git_init, create_gitignore


class TestGitFolderCopying:
Expand Down Expand Up @@ -32,7 +32,7 @@ def test_copy_git_folder(self):
Test copying the .git folder and .gitignore from an old directory to a new one
"""
safe_git_init(FAKE_HOME_DIR)
safe_create_gitignore(FAKE_HOME_DIR)
create_gitignore(FAKE_HOME_DIR, "root-gitignore")
move_git_repo(FAKE_HOME_DIR, BACKUP_DEST_DIR)
assert os.path.isdir(os.path.join(BACKUP_DEST_DIR, '.git/'))
assert os.path.isfile(os.path.join(BACKUP_DEST_DIR, '.gitignore'))
Expand Down
24 changes: 18 additions & 6 deletions tests/test_reinstall_dotfiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ def create_file(parent, name):
with open(file, "w+") as f:
f.write(TEST_TEXT_CONTENT)

def create_git_dir(parent):
git_dir = create_dir(parent, ".git/")
git_objects = create_dir(git_dir, "objects/")
create_file(git_dir, "config")
create_file(git_objects, "obj1")
return git_dir


setup_env_vars()
create_config_for_test()
for directory in DIRS:
Expand All @@ -49,14 +57,13 @@ def create_file(parent, name):
testfolder = create_dir(DOTFILES_PATH, "testfolder1/")
testfolder2 = create_dir(testfolder, "testfolder2/")

# Git dir that should not be reinstalled
git = create_dir(DOTFILES_PATH, ".git/")
git_objects = create_dir(git, "objects/")
create_file(git, "config")
create_file(git_objects, "obj1")
git_dir_should_not_reinstall = create_git_dir(DOTFILES_PATH)
git_dir_should_reinstall = create_git_dir(testfolder2)

# SAMPLE DOTFILES TO REINSTALL
create_file(testfolder2, ".testsubfolder_rc1")
create_file(testfolder2, ".gitignore")
create_file(DOTFILES_PATH, ".gitignore")
create_file(testfolder2, ".testsubfolder_rc2")
create_file(DOTFILES_PATH, ".testrc")

Expand All @@ -76,4 +83,9 @@ def test_reinstall_dotfiles(self):
assert os.path.isfile(os.path.join(testfolder2, '.testsubfolder_rc1'))
assert os.path.isfile(os.path.join(testfolder2, '.testsubfolder_rc2'))

assert not os.path.isdir(os.path.join(FAKE_HOME_DIR, '.git'))
# Don't reinstall root-level git files
assert not os.path.isdir(os.path.join(FAKE_HOME_DIR, ".git"))
assert not os.path.isfile(os.path.join(FAKE_HOME_DIR, ".gitignore"))
# Do reinstall all other git files
assert os.path.isdir(os.path.join(testfolder2, ".git"))
assert os.path.isfile(os.path.join(testfolder2, ".gitignore"))