Skip to content
Open
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
27 changes: 25 additions & 2 deletions ccds-help.json
Original file line number Diff line number Diff line change
Expand Up @@ -355,14 +355,37 @@
"help": {
"description": "Includes common data submodules.",
"more_information": ""
}
}
},
{
"choice": "No",
"help": {
"description": "Empty Python module to start with.",
"more_information": ""
}
}
}
]
},
{
"field": "initialize_git",
"help": {
"description": "Initialize a git repository for version control with an initial commit.",
"more_information": "[About Git](https://git-scm.com/)"
},
"choices": [
{
"choice": "Yes",
"help": {
"description": "Initialize git repository with .gitignore and initial commit.",
"more_information": "Runs 'git init', adds all files, and creates an initial commit."
}
},
{
"choice": "No",
"help": {
"description": "No version control initialization; you can set it up manually later.",
"more_information": ""
}
}
]
}
Expand Down
3 changes: 2 additions & 1 deletion ccds.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@
],
"open_source_license": ["No license file", "MIT", "BSD-3-Clause"],
"docs": ["mkdocs", "none"],
"include_code_scaffold": ["Yes", "No"]
"include_code_scaffold": ["Yes", "No"],
"initialize_git": ["Yes", "No"]
}
9 changes: 4 additions & 5 deletions docs/docs/using-the-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@ You've [created](index.md#starting-a-new-project) your project. You've [read the

Here's a quick guide of the kinds of things we do once our project is ready to go. We'll walk through this example using git and GitHub for version control and jupyter notebooks for exploration, but you can use whatever tools you like.

## Set up version control
## Version Control

Often, we start by initializing a `git` repository to track the code we write in version control and collaborate with teammates. At the command line, you can do this with the following commands which do the following: turn the folder into a git repository, add all of the files and folders created by CCDS into source control (except for what is in the `.gitignore` file), and then make a commit to the repository.
By default, CCDS initializes your project as a git repository with an initial commit. If you chose not to initialize git during setup, or want to reinitialize, you can do this manually at the command line:

```bash
# From inside your newly created project directory
git init
git add .
git commit -m "CCDS defaults"
git commit -m "Initial commit"
```

We usually commit the entire default CCDS structure so it is easy to track the changes we make to the structure in version history.
Whether git was initialized automatically or manually, you now have version control set up for your project. We usually commit the entire default CCDS structure so it is easy to track the changes we make to the structure in version history.

Now that the default layout is committed, you should push it to a shared repository. You can do this through the interface of whatever source control platform you use. This may be GitHub, GitLab, Bitbucket, or something else.

Expand Down
50 changes: 50 additions & 0 deletions hooks/post_gen_project.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import shutil
import subprocess
import sys
from copy import copy
from pathlib import Path

Expand Down Expand Up @@ -120,3 +122,51 @@
# remove any content in __init__.py since it won't be available
generated_path.write_text("")
# {% endif %}

# Initialize git repository if requested
# {% if cookiecutter.initialize_git == "Yes" %}
try:
# Check if git is available
git_check = subprocess.run(
["git", "--version"],
capture_output=True,
text=True,
)

if git_check.returncode == 0:
# Initialize git repository
subprocess.run(["git", "init"], check=True)

# Configure line ending handling (recommended for cross-platform compatibility)
subprocess.run(["git", "config", "core.autocrlf", "input"], check=False)

# Add all files
subprocess.run(["git", "add", "."], check=True)

# Create initial commit
commit_message = "Initial commit from cookiecutter-data-science template"
subprocess.run(
["git", "commit", "-m", commit_message],
check=True,
)

print("\n Git repository initialized with initial commit")
else:
print(
"\n Git not found. Skipping repository initialization.\n"
" Install git and run: git init && git add . && git commit -m 'Initial commit'",
file=sys.stderr,
)
except subprocess.CalledProcessError as e:
print(
f"\n Git initialization failed: {e}\n"
f" You can manually initialize with: git init && git add . && git commit -m 'Initial commit'",
file=sys.stderr,
)
except FileNotFoundError:
print(
"\nGit not found. Skipping repository initialization.\n"
" Install git and run: git init && git add . && git commit -m 'Initial commit'",
file=sys.stderr,
)
# {% endif %}
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def _is_valid(config):
"dataset_storage",
"open_source_license",
"docs",
"initialize_git",
]
multi_select_cyclers = {k: cycle(cookiecutter_json[k]) for k in cycle_fields}

Expand Down
49 changes: 49 additions & 0 deletions tests/test_creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,52 @@ def verify_makefile_commands(root, config):
assert "reformatted" not in stderr_output

assert result.returncode == 0


def test_git_initialization(config, fast):
"""Test that git repository is initialized when requested."""
# Test with git initialization enabled
config_with_git = {**config, "initialize_git": "Yes"}

with bake_project(config_with_git) as project_directory:
git_dir = project_directory / ".git"

# Check that .git directory exists
assert git_dir.exists(), "Git repository should be initialized"
assert git_dir.is_dir(), ".git should be a directory"

# Check that there is an initial commit
result = run(
["git", "log", "--oneline"],
cwd=str(project_directory),
capture_output=True,
text=True,
)

assert result.returncode == 0, "Git log should succeed"
assert "Initial commit" in result.stdout, "Should have initial commit"

# Check that all expected files are tracked
result = run(
["git", "ls-files"],
cwd=str(project_directory),
capture_output=True,
text=True,
)

tracked_files = result.stdout.strip().split("\n")
assert len(tracked_files) > 0, "Should have tracked files"
assert ".gitignore" in tracked_files, ".gitignore should be tracked"
assert "README.md" in tracked_files, "README.md should be tracked"


def test_no_git_initialization(config, fast):
"""Test that git repository is not initialized when not requested."""
# Test with git initialization disabled
config_no_git = {**config, "initialize_git": "No"}

with bake_project(config_no_git) as project_directory:
git_dir = project_directory / ".git"

# Check that .git directory does not exist
assert not git_dir.exists(), "Git repository should not be initialized"