cd my-project
git init -b main
# Create and review .gitignore before staging files.
git add -A
git diff --cached --stat
git diff --cached
git commit -m "Initial commit"This starts version control for a new directory or an existing local codebase. The important pause is before the commit: inspect what is staged so secrets, dependency folders and build output do not become part of the first snapshot.
If the code already lives in a repository on GitHub or another host, clone that repository instead. Initializing a new repository does not retrieve someone else's history.
Choose your starting state
For a new folder:
mkdir my-project
cd my-projectFor an existing unversioned project:
cd my-existing-project
pwdInspect the directory and any enclosing repository before initializing. Running git status here can reveal that the folder is already inside a parent repository. A “not a git repository” result is expected for a genuinely unversioned project. Avoid creating an accidental nested repository.
A first commit of existing code records its state now. It cannot recover development history from before version control existed.
Initialize and choose the branch name
git init -b mainThe -b option is available in Git 2.28 and later. It makes the branch name explicit regardless of the installation's default. To set a preference for future repositories:
git config --global init.defaultBranch mainCurrent Git documentation still describes master as the fallback unless configured otherwise; hosting-service defaults do not determine your local git init default. On an older Git version, initialize first and rename the branch after the first commit:
git branch -m main
git init normally creates .git, which holds local repository metadata. Deleting it can destroy local history and configuration. If you initialize the wrong directory, establish whether a repository existed beforehand and back up anything important before removing metadata; do not use a blanket rm -rf .git as a routine undo command.
Write .gitignore before staging
A Node project might start with:
# Dependencies and build output
node_modules/
dist/
build/
.next/
# Local environment files, with an intentionally sanitized example allowed
.env
.env.*
!.env.example
# Local logs and operating-system files
*.log
.DS_Store
Thumbs.db
Review the patterns for the project. A Python application may also ignore __pycache__/ and its virtual environment; a PHP application often ignores vendor/. Team editor settings may be worth tracking, so do not hide every editor directory automatically. A public .env.example must contain placeholders, never a copied live secret.
Ignore rules do not discover credentials for you. Private keys, database exports, service-account files and other sensitive files may have unrelated names. Inspect the files and the staged diff, and use secret scanning where available.
For pattern precedence and exceptions, see the .gitignore guide.
Stage and inspect the exact first snapshot
git add -A
git status --short
git diff --cached --stat
git diff --cachedgit add updates the index; it does not make a commit. The diff is your chance to catch accidental inclusions. For more deliberate selection, stage named files rather than the entire directory.
Before the first commit, remove accidentally staged files from the index while keeping them on disk:
git rm --cached -- .env
git rm -r --cached -- node_modulesFix the ignore rules and inspect the index again. If a file has changed since staging and Git refuses to remove it, inspect both versions rather than adding -f reflexively. After a repository already has commits, git restore --staged -- path/to/file is another way to unstage a change.
Already committed a credential? Revoke or rotate it first, then follow the sensitive-history cleanup process. Removing it in a later commit leaves older copies in history, and rewriting your repository cannot retract copies already cloned or downloaded.
Set identity and make the first commit
Check the identity this repository will use:
git config user.name
git config user.emailSet repository-local values if needed:
git config --local user.name "Your Name"
git config --local user.email "you@example.com"
git commit -m "Initial commit"Choose the email you intend to expose in commit metadata, such as a hosting provider's noreply address. Use --global only when that identity is correct for all your repositories. If Git reports “Please tell me who you are,” see the identity setup fix.
Create an empty remote and connect it
Create a repository on your host without adding a README, license or starter ignore file. That gives the local history an empty destination.

Choose one authentication method. With SSH:
git remote add origin git@github.com:your-username/my-project.gitOr with HTTPS:
git remote add origin https://github.com/your-username/my-project.gitRun only one of those remote add commands, then inspect the result:
git remote -vSSH uses a key registered with the host. GitHub HTTPS Git access uses a supported credential flow, such as a personal access token through a credential manager or GitHub CLI sign-in, rather than your account password. Do not embed a token in the remote URL or paste it into shell history. See SSH versus HTTPS remotes.
If the remote is already populated, inspect its history first. Starting by cloning it and copying your local files into that checkout is often easier than reconciling two unrelated initial commits. Do not force-push merely to make the first-push error disappear.
Push and set the upstream
git push -u origin mainFor the usual Git configuration, -u records origin/main as the upstream so future git push and git pull have a default branch to use. Confirm the files and commit on the host after the push.
A push error is information about a specific mismatch:
| Error | First check |
|---|---|
remote origin already exists | Inspect git remote -v; change the URL if it is wrong |
src refspec main does not match any | Confirm a commit exists and the local branch is named main |
The current branch has no upstream branch | Set the intended upstream |
failed to push some refs | Fetch and inspect the remote's history before deciding how to integrate |
refusing to merge unrelated histories | The two repositories have different roots; review the reconciliation options |
Permission denied (publickey) | Check SSH key registration and authentication |
Protect the shared branch
Configure branch protection or a repository ruleset on the host, where your plan and repository visibility support it. Useful policies include required pull requests, required CI checks and blocking force pushes or deletion.
Inspect the rules' bypass settings: administrators and designated actors may retain exceptions. A rule only protects what its scope and enforcement settings cover. See protecting main on GitHub for the detailed setup.
FAQ
See also
Sources
Authoritative references this article was fact-checked against.
- Git initialization and branch-name optionsgit-scm.com
- Git index removalgit-scm.com
- GitHub: adding locally hosted codedocs.github.com
- GitHub: removing sensitive repository datadocs.github.com
- GitHub protected branchesdocs.github.com





