Creating a repository takes about fifteen seconds, which is why so many repositories are created badly. The form has half a dozen fields, all of them defaulted, and every default is a decision someone will live with for years.
This lesson covers the three ways to create one — web interface, gh, and pushing an existing local
project — and, more usefully, what each field actually does.
What a GitHub repository actually is
Section titled “What a GitHub repository actually is”A GitHub repository is a bare Git repository on GitHub’s servers, plus a database record carrying everything Git does not model: an owner, a description, visibility, collaborator permissions, feature toggles, and the Issues, pull requests and settings attached to it.
That is why an empty repository still has a URL, a settings page and an Issues tab. There are no commits yet — but the record exists, and everything GitHub adds lives on the record rather than in the Git data.
Consequences worth carrying forward:
- Deleting a local clone does nothing to the repository. It is a separate copy.
- A repository can exist with no commits at all, and it is still a fully functional GitHub object.
git pushtransfers Git objects, and nothing else. Issues, settings and pull requests never travel over the Git protocol. Migrating them requires the API.
The fields, and what they commit you to
Section titled “The fields, and what they commit you to”Every repository belongs to a personal account or an organisation. This is the field people regret most, because it determines the permission model.
A repository owned by you personally has one owner — you. Collaborators can be added individually. There is no concept of teams, and if you disappear, so does administrative access.
A repository owned by an organisation supports teams, role-based permissions, organisation-wide policy and rulesets, and multiple owners. It is also subject to organisation policy, which may restrict what you can do with it.
You can transfer a repository between owners later, and GitHub redirects the old URL. But transfer is a genuine migration — permissions do not follow, and anything referencing the old path by API may need updating. If you know the project belongs to a team, create it under the organisation.
Repository name
Section titled “Repository name”The name becomes part of the clone URL, the Pages URL, package identifiers, and every link anyone makes to it.
Renaming works and old URLs redirect. What does not automatically follow: other people’s configured remotes, hardcoded CI references, package registry names, and documentation elsewhere on the internet.
Practical guidance: lowercase with hyphens, descriptive rather than clever, and no organisation or year encoded into it.
Description
Section titled “Description”One sentence, shown in search results and on the repository page. It is the only context a stranger gets before deciding whether to keep reading, and it costs ten seconds.
Visibility
Section titled “Visibility”Public — anyone can read it. Private — only people you grant access to.
This deserves more than a sentence, and gets a whole lesson. The critical point for now: choose before you push, not after. Making a repository public later exposes the entire history, not just the current state — and history is where secrets hide.
Initialise with a README
Section titled “Initialise with a README”Adds a first commit containing README.md. Convenient, with one consequence: the repository is no
longer empty, so pushing an existing local project needs a merge or a force-push rather than a clean
first push.
Rule of thumb: initialise when creating a new project on GitHub first. Do not initialise when you already have local commits to push.
.gitignore
Section titled “.gitignore”GitHub offers templates per language. Choosing one is worth doing at creation, because the whole
point of an ignore file is to be in place before the files it ignores appear. Adding it after
committing node_modules does not remove node_modules from history.
You can list and inspect the available templates from the terminal:
gh repo gitignore listgh repo gitignore view NodeWhat it doesLists the gitignore templates GitHub offers, then prints one.
Why we run itLets you check what a template actually contains before adopting it, rather than trusting the name.
Expected resultA list of template names, then the contents of the named template.
Licence
Section titled “Licence”For public repositories this matters more than most people realise. Code published without a licence is not open source. Default copyright applies, and strictly speaking nobody has permission to use, modify or redistribute it.
If you want people to use your code, pick a licence at creation. If you are unsure, MIT and Apache-2.0 are the common permissive choices, and Apache-2.0 additionally includes an explicit patent grant. For anything with commercial or legal weight, this is a question for someone qualified rather than a default.
gh repo license listgh repo license view mitDefault branch
Section titled “Default branch”The branch GitHub shows first, and the base branch pull requests target by default. main is
GitHub’s default for new repositories.
Change it early or not at all. Renaming a default branch after people have cloned leaves every existing clone tracking a branch that no longer exists.
Three ways to create one
Section titled “Three ways to create one”The web interface
Section titled “The web interface”Best when you want to see the options, or when you are creating a repository for someone else and want the README, licence and ignore file generated for you.
- Open the new-repository form from the + menu in the top-right of any GitHub page.
- Choose the owner — your account or an organisation you belong to.
- Enter a name and a one-line description.
- Choose visibility.
- Decide whether to initialise with a README,
.gitignoreand licence. - Create the repository.
GitHub then shows the clone URL and a set of quick-start commands. Those commands are correct, but worth understanding rather than pasting — the next section explains what they do.
The GitHub CLI
Section titled “The GitHub CLI”Faster, scriptable, and it can create and clone in one step. This is the method to learn.
gh repo create my-project --public --add-readme --license mit --cloneWhat it doesCreates a public repository under your account, initialises it with a README and an MIT licence, and clones it into the current directory.
Why we run itOne command replaces the form plus a separate clone, and being a command it can go in a script or a project template.
Expected resultA confirmation line with the new repository URL, followed by clone output.
Useful variations:
gh repo create my-project --private --clonegh repo create my-org/team-project --internal --description "Shared tooling"gh repo create my-project --private --source=. --remote=origin --pushThe third form is the important one: it takes the repository you already have locally, creates
the GitHub repository, adds it as origin, and pushes. That is the whole “existing project” flow in
a single command.
Run gh repo create --help to see the current flag set — the CLI gains flags regularly, and the
help output is authoritative in a way that any tutorial, including this one, is not.
Pushing an existing local project
Section titled “Pushing an existing local project”You have a project with commits and no remote. There are two paths.
The manual path:
git remote add origin git@github.com:username/my-project.gitgit branch -M maingit push -u origin mainWhat it doesPoints your local repository at a new empty GitHub repository and pushes the default branch, setting it to track the remote.
Why we run it`-u` records the upstream so later `git push` and `git pull` need no arguments. Without it, every push needs the remote and branch spelled out.
Expected resultObject counting and transfer output, ending with a line confirming the branch is set up to track origin/main.
The CLI path, which does all of it including creating the repository:
gh repo create my-project --private --source=. --remote=origin --pushHTTPS and SSH remote URLs
Section titled “HTTPS and SSH remote URLs”The same repository is reachable by two URL forms:
https://github.com/username/my-project.gitgit@github.com:username/my-project.gitThey differ only in how Git authenticates — covered in the previous lesson. You can switch at any time without touching your history:
git remote set-url origin git@github.com:username/my-project.gitgit remote -vOutput:
origin git@github.com:username/my-project.git (fetch)origin git@github.com:username/my-project.git (push)gh sets this for you according to the Git protocol you selected during gh auth login, which is
one fewer decision to make per repository.
Repository templates
Section titled “Repository templates”Any repository can be marked as a template. Creating from a template copies the files into a brand new repository with a fresh history — a single initial commit, not the template’s commits.
That is the difference from a fork, which preserves history and records a relationship to the original. Templates are for scaffolding; forks are for contribution.
gh repo create my-service --template my-org/service-template --private --cloneTemplates are worth reaching for once you have created the same project skeleton three times.
Importing an existing project from elsewhere
Section titled “Importing an existing project from elsewhere”Moving a project from another host — GitLab, Bitbucket, a self-hosted server — is a Git operation plus a decision about what else you want to bring.
The Git half is a mirror clone, which copies every ref rather than just the default branch:
git clone --mirror https://old-host.example.com/team/project.gitcd project.gitgit push --mirror git@github.com:username/project.gitWhat it doesClones every branch, tag and ref from the old host, then pushes all of them to the new GitHub repository.
Why we run itA normal clone brings the default branch and leaves other branches as remote-tracking refs, so a normal push would publish only part of the history. `--mirror` copies the complete ref namespace.
Expected resultTwo transfers: a full clone, then a push listing every ref created on the new remote.
What a mirror does not bring is everything on the other platform’s side of the boundary: Issues, merge requests, review history, CI configuration specific to that host, and wiki content stored separately. Those need either the source platform’s export tooling or scripted migration through both APIs — the GitHub API cluster covers the receiving half.
Verifying what you created
Section titled “Verifying what you created”It is worth confirming the repository is what you think it is, particularly in a script where nobody is watching:
gh repo view username/my-project --json name,visibility,defaultBranchRef,licenseInfo,isTemplateOutput:
{ "defaultBranchRef": { "name": "main" }, "isTemplate": false, "licenseInfo": { "key": "mit" }, "name": "my-project", "visibility": "PUBLIC"}Structured output like this is the foundation of everything in the CLI cluster — it is the difference between automation that reads data and automation that parses text and breaks when a column width changes.
Repository settings can also be changed after the fact from the terminal, which is often quicker than finding the right settings page:
gh repo edit username/my-project --description "A better description" --enable-wiki=falseSettings worth reviewing after creation
Section titled “Settings worth reviewing after creation”Creation is not the end of configuration. Shortly after, it is worth deciding:
- Which features are on. Issues, Discussions, Wikis and Projects can each be disabled. A repository with an unused, unmonitored Issues tab is worse than one with none.
- Merge strategies. Which of merge, squash and rebase are permitted, and whether branches delete automatically after merge. These shape your history — Rebase vs Merge covers the consequences.
- Branch protection or a ruleset, if anyone other than you will push. The Pull Requests cluster covers this properly.
The first five files
Section titled “The first five files”A repository that will outlive the week benefits from a small set of files, and adding them at creation costs almost nothing.
| File | Why |
|---|---|
README.md | What this is, how to install, simplest use |
LICENSE | Without it, nobody may legally use the code |
.gitignore | Before the artefacts it excludes appear |
CONTRIBUTING.md | If anyone else will contribute |
.github/CODEOWNERS | If ownership is not obvious |
The README is worth more thought than it usually gets: it is the only thing most visitors read, and the first screen should answer what the project is, who it is for, and what the simplest possible use looks like. Everything else can be a link.
Common mistakes
Section titled “Common mistakes”Initialising both ends and force-pushing to fix it. Silently discards the generated README and licence. Create the remote empty when you have local commits.
Publishing without a licence and expecting contributions. No licence means no permission.
Adding .gitignore after the first commit. The ignore file does not retroactively remove
anything already tracked.
Choosing private “for now” and going public later without auditing history. Everything ever committed becomes visible, including the credential in commit 3 that was removed in commit 4.
Naming for the current context. new-api-v2-final ages badly.
Creating under a personal account what belongs to a team. Transfer is possible but is a real migration.
Exercise
Section titled “Exercise”Use a disposable repository — you will delete it at the end.
- Create a repository with
gh repo create, public, with a README and an MIT licence, and clone it. - Confirm the licence and README arrived, and check the default branch name.
- In a separate directory,
git inita new project, make two commits, and publish it withgh repo create --source=. --push. - Switch that second repository’s remote from HTTPS to SSH, or the reverse, and verify with
git remote -v. - Delete both repositories with
gh repo delete, which will ask you to confirm.
The point of step 3 is that it is the flow you will use most in real work: the project exists first, and GitHub comes second.
What you learned
Section titled “What you learned”- A GitHub repository is a bare Git repository plus a record holding everything Git does not model.
- Owner and visibility are the two fields with lasting consequences; name and default branch are cheap to change early and expensive later.
- Initialising with a README conflicts with pushing existing local commits — pick one.
gh repo create --source=. --pushpublishes an existing project in one step.- Templates copy files with fresh history; forks preserve history and a relationship.
- Code with no licence is not open source.