Make your first pull request: Git and GitHub from first commit to merged change
A complete beginner's path through Git and GitHub: set up Git, make commits, ignore files, use branches, push to GitHub, open a pull request, fix a merge conflict and keep a fork up to date.
Git records the history of a folder of files. GitHub is a website that hosts Git repositories and adds collaboration features such as pull requests. This tutorial takes you from installing Git to getting a change merged through a pull request, including the two situations that scare most beginners: a merge conflict and a fork that has fallen behind.
It is written for complete beginners who are comfortable typing commands in a terminal. All Git commands below were run end to end with Git 2.54 on a local machine; in place of GitHub a local bare repository (a repository with no working files, which is what GitHub stores on its servers) stood in, so the outputs you see are real. The GitHub website steps are described from GitHub’s documentation.
What you will build
- A small
recipesrepository with several commits and a.gitignore. - A copy of it on GitHub, connected as the
originremote. - A feature branch that is pushed and merged through a pull request.
- A resolved merge conflict between your branch and someone else’s change.
- A fork that you keep in sync with the original (
upstream) repository.
Before you start
- Git 2.28 or newer (needed for the
init.defaultBranchsetting). Install it from git-scm.com, with your package manager (for examplesudo apt install git), or on macOS with the Xcode Command Line Tools. Check withgit --version. - A free GitHub account, and a way to authenticate pushes: an SSH key added to your account, the GitHub CLI (
gh auth login), or HTTPS with a personal access token or a credential manager. GitHub does not accept your account password for Git over HTTPS. - A terminal and a text editor.
- Time: about 60-90 minutes.
- Knowledge: basic terminal use (
cd,ls, creating files). The concepts you will meet are commits (snapshots), branches (movable labels on commits) and remotes (other copies of the repository).
Step 1: Tell Git who you are
Why: every commit records an author name and email. Set them once per computer with --global. Also set the name of the first branch in new repositories to main, which is what GitHub uses for new repositories, and make git pull merge by default so its behavior is predictable.
git config --global user.name "Alex Example"
git config --global user.email "[email protected]"
git config --global init.defaultBranch main
git config --global pull.rebase false
git config --global --list
Expected output of the last command (plus any settings you had before):
user.name=Alex Example
[email protected]
init.defaultbranch=main
pull.rebase=false
Use the email address you want to appear publicly on GitHub. GitHub also offers a private noreply address in your email settings if you prefer not to publish your real one.
Step 2: Create a repository and make your first commit
Why: git init turns a normal folder into a repository by creating a hidden .git folder. Committing is a two-step process: git add puts changes into the staging area (the next snapshot you are preparing), and git commit saves that snapshot permanently with a message.
mkdir recipes
cd recipes
git init
printf '# Recipes\n\nMy favorite recipes.\n' > README.md
printf 'Pancakes\n========\n\n- 2 eggs\n- 200 g flour\n- 300 ml milk\n' > pancakes.md
git status --short
?? README.md
?? pancakes.md
?? means “untracked”: Git sees the files but is not recording them yet. Stage and commit them:
git add README.md pancakes.md
git commit -m "Add README and pancake recipe"
[main (root-commit) 350c811] Add README and pancake recipe
2 files changed, 9 insertions(+)
create mode 100644 README.md
create mode 100644 pancakes.md
Your commit ID (350c811 here) will be different. Write messages that say what the change does, in a few words, as if completing the sentence “This commit will …”.
Step 3: Ignore files that do not belong in the repository
Why: operating-system junk, logs, build output, virtual environments and anything containing secrets should never be committed. A .gitignore file lists patterns Git should not offer to track. * matches any characters except a slash, and a pattern ending in / matches only directories.
printf '.DS_Store\n*.log\nnotes-private/\n' > .gitignore
touch debug.log
mkdir notes-private && echo "secret" > notes-private/todo.txt
git status --short
?? .gitignore
Only .gitignore itself shows up; debug.log and notes-private/ are ignored. Commit it, then look at the history:
git add .gitignore
git commit -m "Ignore OS junk, logs and private notes"
git log --oneline
8a45360 Ignore OS junk, logs and private notes
350c811 Add README and pancake recipe
Important: .gitignore does not affect files that are already tracked. If you committed a file by mistake, stop tracking it with git rm --cached FILE, add it to .gitignore and commit. If that file contained a password or key, treat the secret as leaked and change it.
Step 4: Create an empty repository on GitHub and push
Why: a remote copy is your backup and the place where others can see and review your work.
- On GitHub, click + in the upper-right corner and choose New repository.
- Name it
recipesand choose public or private. - Do not tick README, .gitignore or license. You already have a local history, and GitHub’s documentation warns that adding these files can cause a merge conflict.
- Click Create repository and copy the URL shown on the quick setup page (SSH looks like
[email protected]:USERNAME/recipes.git, HTTPS likehttps://github.com/USERNAME/recipes.git).
Connect your local repository to it and push. -u remembers origin/main as the upstream of your local main, so later a plain git push or git pull knows where to go:
git remote add origin [email protected]:USERNAME/recipes.git
git remote -v
git push -u origin main
The end of the push output should look like this (the URL is yours):
* [new branch] main -> main
branch 'main' set up to track 'origin/main'.
Reload the repository page on GitHub: your files and both commits are there.
Step 5: Work on a branch
Why: a branch lets you work on one change without disturbing main. Each pull request is normally one branch. git switch -c creates a branch and moves to it.
git switch -c add-omelette
printf 'Omelette\n========\n\n- 3 eggs\n- salt\n' > omelette.md
git add omelette.md
git commit -m "Add omelette recipe"
git push -u origin add-omelette
git branch -a
* add-omelette
main
remotes/origin/add-omelette
remotes/origin/main
The * marks the branch you are on. remotes/origin/... are your local records of the branches on GitHub.
Step 6: Open, review and merge the pull request
Why: a pull request (PR) asks for the commits on your branch to be merged into another branch, usually main. It is where review, discussion and automated checks happen.
- Open your repository on GitHub. A yellow banner offers Compare & pull request for the branch you just pushed; click it.
- Check that base is
mainand compare isadd-omelette. - Write a title and a description that says what changed and why. Click Create pull request, or choose Create draft pull request if it is not ready for review yet.
- Reviewers comment on the Files changed tab. To respond to feedback, just make more commits on the same branch and push; the pull request updates automatically.
- When it is approved, click Merge pull request and confirm. GitHub then offers a Delete branch button.
If you have the GitHub CLI installed, gh pr create --base main --head add-omelette --title "Add omelette recipe" does the same from the terminal.
After the merge, update your local main and remove the finished branch:
git switch main
git pull
git branch -d add-omelette
git log --oneline --graph
* 1f7778f Merge pull request #1 from add-omelette
|\
| * 33d1372 Add omelette recipe
|/
* 8a45360 Ignore OS junk, logs and private notes
* 350c811 Add README and pancake recipe
git branch -d refuses to delete a branch whose commits are not merged yet, which protects you from losing work. If you did not use GitHub’s button, delete the remote branch with git push origin --delete add-omelette.
Step 7: Resolve a merge conflict
Why: when two people change the same lines, Git cannot know which version is right and asks you to decide. This is normal, not an error in your setup.
Start a new branch and change the milk quantity. Meanwhile, a collaborator changes the same line on main on GitHub (to try this alone, edit pancakes.md directly on GitHub’s main branch with the pencil icon).
git switch -c more-milk
# edit pancakes.md: change "300 ml milk" to "350 ml milk"
git commit -am "Use more milk for thinner pancakes"
GitHub will show “This branch has conflicts that must be resolved” on the pull request. Bring the latest main into your branch locally:
git fetch origin
git merge origin/main
Auto-merging pancakes.md
CONFLICT (content): Merge conflict in pancakes.md
Automatic merge failed; fix conflicts and then commit the result.
Open pancakes.md. Git has marked the clash:
<<<<<<< HEAD
- 350 ml milk
=======
- 250 ml milk
>>>>>>> origin/main
The part between <<<<<<< HEAD and ======= is your branch; the part after it is the incoming change. Edit the file into the version you want and delete all three marker lines. For example:
- 300 ml milk (add up to 50 ml more for thinner pancakes)
Then mark the file as resolved, finish the merge and push:
git status
git add pancakes.md
git commit --no-edit
git push -u origin more-milk
git status lists the file under “Unmerged paths” until you git add it. --no-edit accepts Git’s prepared merge message. Open a pull request for more-milk; it now merges cleanly. If you panic in the middle of a conflict, git merge --abort puts everything back to how it was before the merge.
Step 8: Contribute to someone else's project with a fork
Why: you usually cannot push branches to other people’s repositories. A fork is your own copy on GitHub; you push to the fork and open the pull request from there. The original repository is conventionally called upstream, your fork origin.
- On the original project’s GitHub page, click Fork and create the fork under your account.
- Clone your fork, not the original.
- Add the original as a second remote called
upstream.
git clone [email protected]:USERNAME/recipes.git recipes-fork
cd recipes-fork
git remote add upstream [email protected]:ORIGINAL-OWNER/recipes.git
git remote -v
origin [email protected]:USERNAME/recipes.git (fetch)
origin [email protected]:USERNAME/recipes.git (push)
upstream [email protected]:ORIGINAL-OWNER/recipes.git (fetch)
upstream [email protected]:ORIGINAL-OWNER/recipes.git (push)
Then work exactly as in steps 5 and 6: create a branch, commit, git push -u origin BRANCH, and GitHub offers to open the pull request against the original repository.
Step 9: Keep your fork up to date
Why: the original project keeps moving. Start each new branch from a fresh main so your pull requests do not conflict with work that is already merged.
git fetch upstream
git switch main
git merge upstream/main
git push origin main
Updating 9bb9c30..4ffd27e
Fast-forward
pancakes.md | 1 +
1 file changed, 1 insertion(+)
“Fast-forward” means your main had no commits of its own, so Git simply moved it forward. That is why you should never commit directly to main in a fork. GitHub can do the same from the website: on your fork, open the Sync fork menu above the file list and click Update branch; then run git pull locally.
Check that it works
git log --oneline --graph --allshows your commits, the merge commit of the first pull request and the merged conflict resolution.git statusonmainsaysYour branch is up to date with 'origin/main'.after agit pull.- On GitHub, the Pull requests tab, filter Closed, lists your merged pull requests.
- In your fork,
git fetch upstreamfollowed bygit status -sbonmainshows no “behind” count after syncing.
If something goes wrong
! [rejected] main -> main (fetch first)anderror: failed to push some refs. The remote has commits you do not have. Rungit pull, resolve any conflicts, then push again. Do not reach for--forceon a shared branch.fatal: The current branch NAME has no upstream branch.The branch exists only locally. Push it once withgit push -u origin NAME.fatal: not a git repository (or any of the parent directories): .git. You are in the wrong folder.cdinto the repository.- Authentication fails on push. GitHub does not accept your account password for Git operations. Use SSH keys,
gh auth login, or a personal access token with a credential manager. - A file you ignored still shows up as modified. It was committed before you ignored it. Run
git rm --cached FILEand commit.
Clean up
To practice again from scratch, you can delete the local folders and the test repositories. Warning: deleting a local repository folder deletes its entire history if it was never pushed, and deleting a GitHub repository cannot be undone. Check first with git status and git log origin/main..main (no output means nothing is unpushed).
cd ..
git -C recipes status
rm -r recipes recipes-fork
To delete the practice repository on GitHub, open it, go to Settings, scroll to the Danger Zone and choose Delete this repository. Merged branches on GitHub can be removed from the Branches page.
Next steps
- Learn
git restore,git revertandgit reflogto undo mistakes safely. - Try
git rebaseas an alternative to mergingmaininto your branch, and learn when not to use it. - Add a GitHub Actions workflow that runs tests on every pull request.
- Protect
mainwith a branch protection rule that requires a review before merging. - Set up SSH keys if you used HTTPS, so you never type credentials again.
Official documentation
See also
- 0032 First-time Git setup: name, email, default branch, SSH or HTTPS Tell Git who you are, pick sensible defaults, and choose between SSH and HTTPS for GitHub. Includes a small script that applies… Beginner · How-to · Linux, macOS, Windows Script Setup 15 Oct 2026
- 0033 The everyday Git workflow: status, add, commit, push, pull Git moves your changes through three places: the working folder, the staging area, and the commit history. These five commands, plus .gitignore… Beginner · Concept · Any system Article Housekeeping · Setup 15 Oct 2026
- 0034 Merging vs rebasing in Git Merge and rebase both bring commits from one branch into another. Merge keeps history exactly as it happened; rebase replays your commits… Intermediate · Concept · Any system Article Branching & rebasing 15 Oct 2026
- 0038 The pull request workflow on GitHub A pull request asks to merge your branch into another one, with review and automated checks in between. This is the whole… Beginner · How-to · Any system Article Pull requests 15 Oct 2026
- 0041 Fix: CONFLICT (content): Merge conflict in … Two branches changed the same lines and Git cannot choose for you. Open the file, keep the right text, remove the markers,… Intermediate · Fix · Any system Article Branching & rebasing · Common errors 15 Oct 2026
Repos in the wild
- GitHub CLI (gh) cli/cli GitHub's official command-line tool. Create and check out pull requests, manage issues, view Actions runs, and sign in to GitHub from the terminal. GitHub
History
Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.
- commitpedia-admin +14,249 chars