Aller au contenu

Creating a repository

On the Git server, Sam created the shared repository with git clone --bare. On GitHub, a form does the same job, and can also add the first files of the project.

The "Create a new repository" form

Open the form from anywhere on GitHub with the + menu (top right) > New repository, or from the Repositories tab of your profile with the New button.

Field Purpose
Repository template Start from the files of a template repository. Leave No template for an empty project
Owner Your personal account, or an organization you can create repositories in
Repository name Short and memorable. Letters, digits, -, _ and .; other characters, such as spaces, are replaced by -
Description (optional) One sentence, shown in the About box and in search results
Visibility Public or Private (see below)
Add README Create a README.md file containing the repository name as a title
Add .gitignore Create a .gitignore file from a template, for a language or tool
Add license / Choose a license Create a LICENSE file with the text of a common license

The form states which branch will be created: main, unless you changed the default in your settings (Settings > Repositories). Finally, click Create repository.

The link Import a repository, at the top of the form, copies a repository from another hosting service by its URL, history included.

Sam fills in the form for the homelab:

Field Value
Owner / Repository name sam-rivera / homelab
Description Notes and inventory of my home servers
Visibility Private
README, .gitignore, license None: the repository will receive the existing history from the desktop

Public or private

Visibility Who can see it Who can change it
Public Anyone on the internet, even without an account You, and the people you choose
Private Only you, and the people you give access to You, and the people you choose
Internal (enterprise organizations only) Every member of the enterprise The people chosen in the organization

In all cases, you choose who can commit: being able to read a public repository does not give the right to change it. Visibility can be changed later in Settings > General > Danger Zone > Change visibility. Private repositories and collaborators are covered in detail on Repository access and collaborators.

Public means public forever

Anything pushed to a public repository can be copied by anyone, immediately: making it private later does not remove the copies. Never push passwords, tokens, or personal data, even "just for a minute". The homelab inventory lists the internal network of a house: a good reason for Sam to keep it private.

The initial files

The three checkboxes create the first commit of the repository, called Initial commit, with the selected files.

README

The README.md file presents the project. GitHub displays it on the repository page, below the file list. It is so important that it gets its own page.

.gitignore

A .gitignore file lists the files that Git must not track: generated files, local configuration, secrets. GitHub provides templates for most languages and tools. The Python template, for example, contains (among many others):

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[codz]

# Environments
.env
.envrc
.venv
Pattern Matches
__pycache__/ Any directory named __pycache__, at any depth
*.py[codz] Any file ending with .pyc, .pyo, .pyd or .pyz
.env A file or directory named .env: environment variables, often secrets
# ... A comment

Ignored files do not appear in git status, and git add . skips them. In a clone of a repository with a Python .gitignore, after running a script and creating a .env file with a password:

$ git status
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    scripts/

nothing added to commit but untracked files present (use "git add" to track)
$ git status --ignored
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    scripts/

Ignored files:
  (use "git add -f <file>..." to include in what will be committed)
    .env
    scripts/__pycache__/

nothing added to commit but untracked files present (use "git add" to track)

To find out which rule ignores a file, use git check-ignore -v. It prints the file, line number, and pattern of the rule; a file that is not ignored prints nothing:

$ git check-ignore -v .env scripts/__pycache__/backup.cpython-314.pyc scripts/backup.py
.gitignore:153:.env .env
.gitignore:2:__pycache__/   scripts/__pycache__/backup.cpython-314.pyc

Adding an ignored file by name is refused, unless forced with -f:

$ git add .env
The following paths are ignored by one of your .gitignore files:
.env
hint: Use -f if you really want to add them.
hint: Disable this message with "git config set advice.addIgnoredFile false"

.gitignore only affects untracked files

A file that is already committed stays tracked, even if a new rule matches it. To stop tracking it, remove it from the index with git rm --cached <file>, then commit. If it contained a secret, the secret is still in the history: change the password or revoke the token.

License

Without a license, a public repository is visible, but the default copyright law applies: nobody may legally reuse, modify, or share the code. A license tells others what they can and cannot do with your work. GitHub offers the most common ones:

License In short
MIT Do almost anything, as long as the copyright notice is kept. Very permissive
Apache 2.0 Permissive like MIT, with an explicit patent grant
GPL 3.0 Copies and derived works must stay open source under the same license (copyleft)

GitHub detects the license and displays it in the About box. choosealicense.com helps to pick one. A private project such as the homelab does not need one.

Starting from an empty repository

When no file is selected, the repository is created empty: no commit, no branch. GitHub then shows a Quick setup page with the repository URL (HTTPS or SSH) and the commands for the two usual situations.

...or create a new repository on the command line, when the project does not exist yet:

echo "# homelab" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/sam-rivera/homelab.git
git push -u origin main

...or push an existing repository from the command line, Sam's situation:

git remote add origin https://github.com/sam-rivera/homelab.git
git branch -M main
git push -u origin main
Command Purpose
git remote add origin <url> Register the GitHub repository as the remote origin
git branch -M main Rename the current branch to main, even if a branch with that name already exists. Useful when Git created master
git push -u origin main Send main to GitHub, which creates the branch there, and set the upstream

These are the commands from Intermediate Git: GitHub is just another remote. The first push creates main on GitHub:

$ git push -u origin main
To https://github.com/sam-rivera/homelab.git
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.

Over HTTPS, the push asks for your username and a personal access token, not your password: see Authenticating with personal access tokens.

Why an empty repository for an existing project

If Sam had ticked Add README, the GitHub repository would start with its own Initial commit, unrelated to the history of the desktop. The first push is then rejected, like any push to a remote that has commits you do not have:

$ git push -u origin main
To https://github.com/sam-rivera/homelab.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'https://github.com/sam-rivera/homelab.git'

And the usual fix, pulling first, fails too, because the two histories have no commit in common:

$ git pull origin main
From https://github.com/sam-rivera/homelab
 * branch            main       -> FETCH_HEAD
 * [new branch]      main       -> origin/main
fatal: refusing to merge unrelated histories

It can be fixed with git pull --allow-unrelated-histories origin main, which creates a merge commit joining the two histories. But the simplest is to avoid it: to publish an existing repository, create the GitHub repository empty. Add a README, .gitignore, or license when the project starts on GitHub.

Managing a repository after creation

The repository page shows the files of the default branch, and the About box (edit it with the gear icon) for the description, a website, and topics (keywords such as homelab, raspberry-pi).

Settings > General holds the rest:

Setting Effect
Repository name > Rename Rename the repository. GitHub redirects the old URL, but update your remotes with git remote set-url origin <new-url>
Template repository Allow others to create new repositories from this one's files
Default branch Choose which branch is shown and used as the base for pull requests
Danger Zone > Change visibility Switch between public and private
Danger Zone > Archive this repository Make it read-only, but keep it
Danger Zone > Delete this repository Delete it, after typing its full name to confirm

Summary

Action Where
Create a repository + > New repository
Add the first files Add README, Add .gitignore, Add license in the form
Publish an existing local repository Create it empty, then git remote add origin <url> and git push -u origin main
See which rule ignores a file git check-ignore -v <file>
List ignored files git status --ignored
Rename, change visibility, delete Settings > General

Common mistakes

  • Initializing the repository with a README before pushing an existing project. The first push is rejected, and the pull refuses to merge unrelated histories.
  • Making a repository public "for a moment". Anything published can be copied immediately.
  • Expecting .gitignore to hide a file that is already committed. Rules only apply to untracked files.
  • Publishing a public project without a license. Nobody can legally reuse it.

Hands-on labs

Three labs, from guided to more autonomous. Labs 1 and 3 need your GitHub account; Lab 2 runs entirely in the terminal. Replace <username> with your GitHub username.

Lab 1: create a repository with its initial files

Objective: create a repository with a README, a .gitignore, and a license, then check locally that the .gitignore works.

Prerequisites and initial state: a GitHub account, Git installed. No repository named homelab-practice-1 in your account.

Setup:

mkdir -p ~/git-practice/create && cd ~/git-practice/create

Tasks:

  1. On GitHub, create a public repository homelab-practice-1, with the description Practice repository, a README, the Python .gitignore template, and the MIT license.
  2. On the repository page, note the number of commits and its message, the files, and what the About box shows.
  3. Open .gitignore on GitHub, and find the lines for __pycache__/ and .env.
  4. In the terminal, clone the repository into ~/git-practice/create (a public repository can be cloned without signing in).
  5. In the clone, create scripts/backup.py containing print("backup"), a file .env containing NAS_PASSWORD=secret, and run python3 scripts/backup.py (or, without Python, create an empty scripts/__pycache__/backup.pyc by hand).
  6. Check which files Git sees, which ones it ignores, and which rule ignores each of them.

Expected result and verification:

  • The repository has 1 commit, Initial commit, with .gitignore, LICENSE and README.md. The About box shows the description, Readme, and MIT license.
  • git status only shows scripts/ as untracked; git status --ignored lists .env and scripts/__pycache__/ under Ignored files.
  • git check-ignore -v .env prints a line of the form .gitignore:<line>:.env .env.
Solution
# 4. Clone
cd ~/git-practice/create
git clone https://github.com/<username>/homelab-practice-1.git
cd homelab-practice-1
ls -a                               # .  ..  .git  .gitignore  LICENSE  README.md

# 5. A script, its compiled cache, and a secret
mkdir scripts
echo 'print("backup")' > scripts/backup.py
echo "NAS_PASSWORD=secret" > .env
python3 scripts/backup.py
python3 -m py_compile scripts/backup.py   # creates scripts/__pycache__/

# 6. What Git sees and ignores
git status
git status --ignored
git check-ignore -v .env scripts/__pycache__/*
  • python3 scripts/backup.py alone does not create __pycache__: Python only caches imported modules. py_compile forces it.
  • The exact line numbers depend on the version of GitHub's template; the pattern shown is what matters.

Clean up when you are done: rm -rf ~/git-practice/create. Keep homelab-practice-1 on GitHub if you want to reuse it on the next page; otherwise delete it in Settings > General > Danger Zone > Delete this repository.

Lab 2: publish an existing project, rehearsal

Objective: run the Quick setup commands of an empty GitHub repository, against a local bare repository standing in for GitHub, from a repository whose branch is called master.

Prerequisites and initial state: Git installed. The setup creates, in ~/git-practice/quick-setup, an empty bare repository fake-github/homelab.git, and a local project homelab with one commit on a branch named master.

Setup:

mkdir -p ~/git-practice/quick-setup && cd ~/git-practice/quick-setup
git init -q --bare -b main fake-github/homelab.git
git init -q -b master homelab
cd homelab
git config user.name "Sam Rivera"
git config user.email "[email protected]"
printf 'hostname,ip,role\npi-dns,192.168.1.10,dns\n' > inventory.csv
git add . && git commit -q -m "Create inventory"

Tasks:

  1. In homelab, check the current branch name.
  2. Run the three commands of ...or push an existing repository from the command line, with ../fake-github/homelab.git as the URL.
  3. Check the branch name again, and the upstream of the branch.
  4. Make a change to inventory.csv, commit it, and push it with a plain git push.
  5. Clone the "GitHub" repository into check, and verify that it contains both commits.

Expected result and verification:

  • At task 1, the branch is master; after task 2, it is main.
  • The push prints * [new branch] main -> main and branch 'main' set up to track 'origin/main'.
  • git branch -vv shows [origin/main].
  • Task 4 works without arguments, thanks to the upstream. In check, git log --oneline shows the two commits.
Solution
cd ~/git-practice/quick-setup/homelab
git branch                          # * master

# 2. Quick setup commands
git remote add origin ../fake-github/homelab.git
git branch -M main
git push -u origin main             # * [new branch] main -> main

# 3. Branch and upstream
git branch -vv                      # * main ... [origin/main] Create inventory

# 4. Next pushes are short
echo "nas,192.168.1.20,storage" >> inventory.csv
git commit -am "Add NAS"
git push

# 5. Check from a fresh clone
cd ..
git clone fake-github/homelab.git check
git -C check log --oneline
  • On GitHub, the URL would be https://github.com/<username>/homelab.git, and the push would ask for credentials: the rest is identical.
  • git branch -M main is why the quick setup works whatever your local branch is called.

Clean up when you are done: rm -rf ~/git-practice/quick-setup.

Lab 3: the README that got in the way

Objective: reproduce the classic mistake of initializing the GitHub repository with a README before pushing an existing project, then repair it; and decide how Sam should create the real repository.

Prerequisites and initial state: Git installed. The setup creates a "GitHub" repository in ~/git-practice/unrelated that already contains an Initial commit with a README (as if Add README had been ticked), and a local homelab project with its own history.

Setup:

mkdir -p ~/git-practice/unrelated && cd ~/git-practice/unrelated
git init -q --bare -b main fake-github/homelab.git
git clone -q fake-github/homelab.git tmp 2>/dev/null
cd tmp
git config user.name "GitHub"
git config user.email "[email protected]"
echo "# homelab" > README.md
git add . && git commit -q -m "Initial commit" && git push -q origin main
cd .. && rm -rf tmp
git init -q -b main homelab
cd homelab
git config user.name "Sam Rivera"
git config user.email "[email protected]"
git config pull.rebase false
printf 'hostname,ip,role\npi-dns,192.168.1.10,dns\n' > inventory.csv
git add . && git commit -q -m "Create inventory"

Tasks:

  1. In homelab, add ../fake-github/homelab.git as origin, and push main with an upstream. Read the error.
  2. Pull main from origin, as the hint suggests. Read the error.
  3. Repair the situation so that the push succeeds, without losing either commit.
  4. Answer, for the real repository: which visibility, and which initial files, should Sam choose for sam-rivera/homelab? And for a new public project with no files yet?

Expected result and verification:

  • Task 1: ! [rejected] main -> main (fetch first). Task 2: fatal: refusing to merge unrelated histories.
  • After task 3, git log --oneline --graph shows a merge commit joining Initial commit and Create inventory, and the push succeeds.
  • Task 4: private, with no initial files (an empty repository); for a new public project, a README, a .gitignore for its language, and a license.
Solution
cd ~/git-practice/unrelated/homelab

# 1. Rejected: the remote has a commit we do not have
git remote add origin ../fake-github/homelab.git
git push -u origin main             # ! [rejected] main -> main (fetch first)

# 2. The histories share no commit
git pull origin main                # fatal: refusing to merge unrelated histories

# 3. Join them explicitly, then push
git pull --allow-unrelated-histories --no-edit origin main
git log --oneline --graph
git push -u origin main
  • --allow-unrelated-histories is safe here because the README does not conflict with any local file. If both sides had a README.md, you would resolve a conflict as usual.
  • The alternative, when the GitHub repository has nothing worth keeping, is to delete it and create it again, empty.
  • The homelab inventory describes a private network: private visibility, and no initial files since the history already exists.

Clean up when you are done: rm -rf ~/git-practice/unrelated.