Skip to content

Git & GitHub - Complete Reference

SAP Implementation Consulting & ERP - Startupistan Germany · Block I · study notes for revision.


Everyone has done the homemade version of this: report-final.docx, report-final2.docx, report-FINAL-really.docx. It half-works until I can’t remember which copy is newest, can’t see what actually changed between two of them, and delete the wrong one with no undo. On a team it gets worse - two people edit the same file and the second save silently wipes out the first.

Version control is that same instinct done properly. It watches a folder and, whenever I say so, records a snapshot of everything, in order, each with a note and a timestamp. In return I get four things:

  • History - every save point kept, so the project reads like a diary of what changed and why.
  • Undo - any file, or the whole project, can go back to an earlier save point. Nothing I recorded is lost by a later edit.
  • Parallel work - several people (or just me, trying two ideas) work at once without trampling each other.
  • Accountability - every change is signed, so “who touched this, and why?” always has an answer.

It is not a big-team-only tool. Solo, the undo and the history alone earn their keep - future me next month has forgotten everything, and the history is how I remember.


Git vs GitHub - the one distinction to keep straight

Section titled “Git vs GitHub - the one distinction to keep straight”

The two names are said together so often that beginners treat them as one thing. They are two things, made by different people, and the first works completely without the second.

GitGitHub
What it isA program installed on my computerA website and cloud service
Where it runsLocally, on my machineOn the internet
Its jobRecords history, restores versions, manages branchesHosts Git projects so others can see and contribute
Needs internet?No - works fully offlineYes
Needs an account?NoYes
Born20052008

One question keeps it straight: “where does it happen?” Recording history happens on my machine, with Git, offline. Sharing that history and discussing changes happens in the cloud, on GitHub. If the internet vanished tomorrow, Git would keep working and GitHub would not.

GitHub is not the only host - GitLab, Bitbucket, and Azure DevOps do the same job. Every command below works identically against all of them, because the commands belong to Git, not to the website. Learning Git is the transferable skill; GitHub is just the meeting point these notes use.


This is the one piece of theory the whole tool rests on. Every command I learn moves work between three (really four) places, always in the same direction.

Working directorythe files I edit
→
Staging areawhat goes in the next save
→
Repositorypermanent history (.git)
→
Remotethe cloud twin (GitHub)
Work flows one way: edit → stage what belongs together → commit it into history → push it to the cloud.
  • Working directory - the folder I see; my actual files in their current state. Where I edit.
  • Staging area - a waiting room between my edits and the history. Here I gather exactly the changes that belong in the next save point. Nothing here is permanent yet.
  • Repository - the recorded history, kept inside a hidden .git folder. Once a change is committed here, it is a permanent save point. (A repository, or repo, is just a folder plus its full recorded history.)
  • Remote - a copy of the repository hosted elsewhere (GitHub). Covered further down.

Why two steps to save, not one? The staging area is Git’s best feature in disguise: it lets me choose what goes into each save point. If I fixed a typo in one file and half-rewrote another, I can commit the finished typo fix now and leave the unfinished rewrite out of history until it is ready. Save points stay clean and honestly labelled.

Every file sits in exactly one of four states, and cycles through them as I work: untracked (brand new, Git has never been told to care), unmodified (tracked, identical to the last save point), modified (tracked, changed since the last save point, not yet staged), staged (in the box, ready for the next commit).


First-time setup - one handshake per machine

Section titled “First-time setup - one handshake per machine”

Every save point is signed with an author name and email, forever. Git refuses to guess these, so I tell it once and it remembers for every project on this machine. --global means “for every project on this computer.”

Terminal window
# Sign my commits (use the SAME email as my GitHub account)
git config --global user.name "My Name"
git config --global user.email "me@example.com"
# Make every new repo start on a branch called "main" (matches GitHub)
git config --global init.defaultBranch main
# Verify a setting by asking for it by name
git config user.name

No output means it worked. The email is not secret - it becomes visible in the project’s history once I publish, which is exactly how work gets credited to my profile. Typed something wrong? Run the same line again with the right value; the new answer replaces the old.


This is the rhythm of every working day: edit → status → add → commit, with log, diff, and restore as the read-and-rescue tools around it.

  1. Create the repository - once per project. init is short for initialize (“set up for the first time”). Run it once, in the project’s top folder.

    Terminal window
    cd my-project
    git init
    Initialized empty Git repository in /home/me/my-project/.git/

    That hidden .git folder is the repository - every save point lives in there, and it travels with the folder if I copy or move it. Never edit or delete it by hand.

  2. Check the state - before and after everything. git status reports what is untracked, modified, or staged, and which branch I am on. It changes nothing, costs a second, and makes Git’s invisible bookkeeping visible.

    Terminal window
    echo "The Corner Bakery" > notes.txt
    git status
    On branch main
    No commits yet
    Untracked files:
    (use "git add <file>..." to include in what will be committed)
    notes.txt
  3. Stage what belongs together. git add copies a file’s current content into the staging area. Silence means success - confirm with status (the file turns from red/untracked to green/staged).

    Terminal window
    git add notes.txt # one file - the deliberate default
    git add notes.txt menu.txt # several files
    git add . # everything below here - convenient, but check status first

    Staging snapshots the file as it is right now. If I edit it again afterwards, that newer edit is not in the box yet - status will list the same file as both staged and modified. Fix: git add it again.

  4. Record the snapshot. git commit -m "message" seals everything staged into one permanent save point. -m is the message, in quotes right after it.

    Terminal window
    git commit -m "Add bakery notes file"
    [main (root-commit) f4a2c1e] Add bakery notes file
    1 file changed, 1 insertion(+)
    create mode 100644 notes.txt

    That f4a2c1e is the commit’s short identifier (its address). A commit is a permanent, addressable snapshot, signed with my name and email, stamped with the time, labelled with my message. It is never silently lost or overwritten - the stack only grows.

  5. Read the history. git log walks the commits newest-first; --oneline is the compact story view.

    Terminal window
    git log --oneline
    7b9e0d2 (HEAD -> main) Add Saturday opening hours
    f4a2c1e Add bakery notes file

    HEAD is Git’s word for “where I am standing right now” - normally the tip of the current branch. If the log fills more than a screen it opens in a pager: press q to quit (the classic “my terminal is stuck” moment, pre-solved).

  6. See the exact changes. git diff shows line-by-line what changed in the working directory since the last commit and is not yet staged. + lines were added, - lines removed, plain lines are unchanged context. This diff format is the universal language of code review.

    Terminal window
    git diff
    diff --git a/notes.txt b/notes.txt
    @@ -1,2 +1,3 @@
    The Corner Bakery
    Open Saturdays from 8:00
    +Closed on public holidays
  7. Rescue a file - the payoff of committing. Damaged a file (emptied it, deleted a paragraph, an hour of edits I want gone)? git restore throws away the uncommitted changes and returns the file to its last save point.

    Terminal window
    git status # confirms the file is "modified" - work is safe in the last commit
    git restore notes.txt # discard the damage, back to the last save point
    git restore --staged menu.txt # take a file back OUT of the box (content untouched)

    The honest warning: restore discards current uncommitted changes - that is its whole job. Glance at git diff first if unsure. It never touches committed history; commits are what it rescues from. The more often I commit, the closer my nearest save point always is.

The message is for humans reading later - most often future me, three weeks on, having forgotten everything. The industry conventions:

  • Imperative mood - “Add weekend specials”, “Fix Saturday opening time”, not “added” or “adding”. Read it as finishing the sentence “this commit will…”.
  • Say what, and why when useful - “Increase espresso price to match supplier costs” beats “change price” beats “stuff”.
  • Short first line - under ~50 characters; it is the line every history view shows.

And commit small and often: one logical change per commit (one bug fixed, one section written), not “everything I did today”. A history of small labelled steps is a readable story, gives surgical undo, and is genuinely reviewable; one thousand-line “did stuff” commit gets skimmed and rubber-stamped.


.gitignore - files that stay out of history

Section titled “.gitignore - files that stay out of history”

Not everything in a project folder belongs in its history. A .gitignore is a plain text file at the repo’s top folder listing patterns Git should never track - one pattern per line. Ignored files just stop appearing as untracked; Git treats them as invisible (they stay on disk, untouched).

.gitignore
# Generated files - regenerable, so recording them is noise
*.log
dist/
temp/
# Dependencies - reinstallable from the project's recipe file
node_modules/
# Secrets - history is permanent and, once published, public
.env

The .gitignore file itself is tracked and committed, so the whole team shares the same rules:

Terminal window
git add .gitignore
git commit -m "Add gitignore for logs, build output and secrets"

Everything so far was local. A remote repository is a copy of my repo hosted elsewhere - for us, on GitHub. The two are twins I keep in sync deliberately: push sends my local commits up, pull brings remote commits down. Nothing syncs by itself, ever.

A repo refers to its remotes by nickname, and the near-universal nickname for a project’s main remote is origin - nothing more than a saved address with a short name, like a contact in my phone. git push origin main reads as “push branch main to the remote nicknamed origin”.

  1. Create an empty repo on GitHub. On github.com: the + menu → New repository. Give it a short, lowercase, hyphenated name (bakery-notes, not test123). Choose Public. Leave every initialization checkbox unchecked - no README, no .gitignore, no license.

  2. Save the remote’s address under the nickname origin. Copy the SSH address from the repo’s setup page (it starts git@github.com:, not https://). This only writes a line into local settings - no network, no upload, no validation yet.

    Terminal window
    git remote add origin git@github.com:my-username/bakery-notes.git
    git remote -v # verify - lists the saved remotes (-v = verbose)
    origin git@github.com:my-username/bakery-notes.git (fetch)
    origin git@github.com:my-username/bakery-notes.git (push)
  3. Try to push - and watch it fail (on purpose). The first push hits a locked door:

    Terminal window
    git push -u origin main
    git@github.com: Permission denied (publickey).
    fatal: Could not read from remote repository.

    This is not broken - it is denied. GitHub is saying “I don’t know who you are, and I expect you to prove it with a public key, which you haven’t shown me.” GitHub stopped accepting account passwords from Git in 2021 - a password typed on every push is a password constantly exposed. Pushing needs a machine-grade credential: an SSH key.

  4. Set up an SSH key - once per machine. An SSH key pair is two matched files: a private key that never leaves my machine, and a public key I hand to GitHub once. From then on GitHub can verify requests come from the machine holding the private twin, with no secret crossing the wire.

    Terminal window
    # 1. Generate the pair (press Enter through all prompts:
    # accept the default file location, leave the passphrase empty)
    ssh-keygen -t ed25519 -C "me@example.com"
    # 2. Print the PUBLIC key (the .pub file - the ONLY one that ever leaves the machine)
    cat ~/.ssh/id_ed25519.pub

    Copy that whole line (starts ssh-ed25519 …). On github.com: profile picture → Settings → SSH and GPG keys → New SSH key, title it after the machine, paste, Add SSH key. Then test:

    Terminal window
    ssh -T git@github.com
    Hi my-username! You've successfully authenticated, but GitHub does not provide shell access.

    The “successfully authenticated” line is the goal. (Say yes to the one-time authenticity question on first contact. Keeping the default filename in step 1 is what lets Git find the key by itself, with zero extra config.)

  5. Push for real. The same command now works. -u (for upstream) is needed once per branch, on its first push - it remembers the pairing so later pushes shrink to one word.

    Terminal window
    git push -u origin main
    To github.com:my-username/bakery-notes.git
    * [new branch] main -> main
    branch 'main' set up to track 'origin/main'.

    From the second push onward, just:

    Terminal window
    git push # sends committed work up; uncommitted edits stay home
  6. Pull to stay current. git pull fetches new commits from the remote’s paired branch and merges them into my local one. Fast-forward means the easy case: the remote was simply ahead and my branch caught up, nothing to weave.

    Terminal window
    git pull

    The professional bracket around every work session: pull before I start (build on the newest state), push when I stop (share, back up, make visible).

git clone downloads a complete copy of a remote repo - all files, the entire history, with origin already wired up (no git remote add needed). It is the standard first command on a new machine, a new team, a new job.

Terminal window
git clone git@github.com:my-username/bakery-notes.git
cd bakery-notes

A branch is an independent line of development inside one repository. Commits on one branch touch no other branch; each line grows its own history until I deliberately merge them. Every repo starts with one branch, main.

The universal convention: main is the stable line - what’s on it works, it’s the version I’d show anyone at any moment. Changes are made on short-lived branches - new work, experiments, fixes each get their own branch, a safe workspace where nothing I do can disturb main. Done and reviewed → merge in. Dead end → delete the branch, and main never knew.

Terminal window
git branch # list branches; the * marks where I am
git switch -c weekend-specials # create a new branch AND move onto it (-c = create)
git switch main # move to an existing branch

Switching transforms the working directory in place - the files in my folder change to match the branch I land on. One folder, many parallel states. Commits always land on the branch I’m standing on, so the single most useful habit is reading the first line of git status (On branch …) before I work and before I commit.

The everyday branch loop is the same edit → add → commit → push I already know; -u on the branch’s first push:

Terminal window
git switch -c add-contact-info
# ...edit files...
git add bakery-notes.txt
git commit -m "Add phone number to notes"
git push -u origin add-contact-info # first push of THIS branch

A pull request (PR) is a request, made on GitHub, to merge one branch into another - almost always my branch into main. But the merge is only the ending; the middle is the point. A PR opens a space where the change sits on display as a readable diff, and people can read exactly what would change, discuss it (comments anchored to exact lines), and approve it - or send it back for another round first. That is code review as the industry practises it.

Decode the name once: I am requesting that the project pull my branch’s changes in. (GitLab calls it a merge request - same machinery.)

  1. Push the branch, then open the PR - via the link Git prints in the push output, the Compare & pull request banner, or the Pull requests tab → New pull request (base main, compare my-branch).

  2. Write a title and description - commit-message discipline, one level up. Title: what it does, imperative, one line. Description: what and why, honestly, plus anything the reviewer should know.

  3. Review in the Files changed tab - the whole change as one diff. Hover any line for a blue + to leave a comment anchored right there. Useful comments are specific and kind: point at the line, say what and why, suggest rather than command.

  4. Merge with the green Merge pull request → Confirm merge. My branch’s commits join main on the remote; the PR closes as merged, its conversation kept forever as documentation. Take the Delete branch button - the branch was scaffolding, its commits now live in main.

  5. Close the loop locally - the merge happened on the remote, so my local main learns of it by pulling:

    Terminal window
    git switch main
    git pull

git merge - what the button runs underneath

Section titled “git merge - what the button runs underneath”

The Merge button isn’t magic; it runs git merge. The grammar trips everyone once: I stand on the receiving branch and name the branch being brought in.

Terminal window
git switch main # stand on the receiver
git merge weekend-specials # bring this branch's commits in
Updating 9c2d7e1..5d3a9f2
Fast-forward
bakery-notes.txt | 1 +

Fast-forward again: main hadn’t moved since the branch was created, so Git just slid main’s label forward - no weaving. When both branches have moved, Git weaves them into a merge commit (a save point with two parents). In practice I use the button - it keeps the review step attached - and knowing the command means the button is a convenience I understand, not a dependency I can’t explain.

A merge conflict happens in exactly one situation: both branches changed the same lines, differently. Git refuses to guess which version is wanted, so it stops and asks. That’s the whole event - not breakage, not loss; a machine declining a judgment call that is mine to make. (Different files, or different parts of the same file, merge silently and correctly.)

  1. The merge pauses. Git marks the disputed spot inside the file.

    Terminal window
    git switch main
    git merge price-update
    Auto-merging bakery-notes.txt
    CONFLICT (content): Merge conflict in bakery-notes.txt
    Automatic merge failed; fix conflicts and then commit the result.
  2. Open the file and read the conflict markers. Between <<<<<<< HEAD and ======= is the version of the branch I’m on (HEAD - here main). Between ======= and >>>>>>> is the incoming branch’s version. Git is showing me both answers and asking: which?

    The Corner Bakery
    <<<<<<< HEAD
    Espresso: 2.60
    =======
    Espresso: 2.80
    >>>>>>> price-update
  3. Decide - deciding is the fix. Edit the file to contain exactly what should be true, deleting all three marker lines.

    The Corner Bakery
    Espresso: 2.80
  4. Tell Git the question is answered - the same add + commit loop I already own. The merge completes; history records both lines and the human decision that joined them.

    Terminal window
    git add bakery-notes.txt
    git commit -m "Merge price-update, keeping new espresso price"

Conflicts are rare when I keep the anti-conflict habits: short-lived branches, small focused changes, and pull before I start. VS Code also recognises the markers and shows accept-this-version buttons above them.


GitHub Pages is free website hosting built into every GitHub repo: tell GitHub to serve the repo’s files as a website, and it does, at an address like https://my-username.github.io/repo-name/. When someone visits, GitHub looks for a file called index.html - the web’s convention for “the front door of a site.”

The workflow is exactly the branch loop from above, plus one settings step:

  1. On a branch, add an index.html at the repo’s top folder - even a placeholder is enough to go live:

    <!DOCTYPE html>
    <html>
    <body>
    <h1>The Corner Bakery</h1>
    <p>Fresh bread, honest coffee.</p>
    </body>
    </html>
  2. Commit, push, PR, merge, and pull main back down - the loop that is now reflex.

    Terminal window
    git switch -c add-homepage
    git add index.html
    git commit -m "Add homepage placeholder"
    git push -u origin add-homepage
  3. Enable Pages. On the repo page: Settings → Pages → under Build and deployment set Source to Deploy from a branch, choose branch main and folder / (root), Save.

  4. Wait a minute or two, refresh, and a banner shows Your site is live at https://my-username.github.io/bakery-notes/. It opens from any phone on earth.

From here it’s edit → commit → push → live: change index.html, push through the loop, wait a minute, refresh - the change is there. That hand-built pipeline is the same shape as any real deployment; the professional version just adds automation to the parts I now do by hand.


Every command from these notes, in one place, grouped by the stage of work it belongs to.

Setup - once per machine

CommandWhat it doesTypical usage
git --versionConfirm Git is installedgit --version
git config --global user.name "…"Set the name that signs commitsOnce, first-time setup
git config --global user.email "…"Set the email that signs commits (match GitHub)Once, first-time setup
git config --global init.defaultBranch mainMake new repos start on mainOnce, first-time setup
git config user.nameRead a setting back to verifyAny time
ssh-keygen -t ed25519 -C "email"Generate an SSH key pairOnce per machine
ssh -T git@github.comTest that GitHub recognises this machineAfter adding the public key

Snapshotting - the everyday save loop

CommandWhat it doesTypical usage
git initTurn the current folder into a repositoryOnce, in a project’s top folder
git add <file>Stage a file for the next commitAfter editing, before committing
git add .Stage everything below the current folderAfter checking git status first
git commit -m "message"Seal staged changes into a permanent save pointOnce per logical change
.gitignoreList file patterns Git should never trackCommitted once, updated as needed

Inspecting - looking without changing

CommandWhat it doesTypical usage
git statusShow what’s untracked, modified, staged, and the branchBefore and after everything
git logList commits, newest first, with full detailReviewing history (q to quit pager)
git log --onelineCompact one-line-per-commit historyQuick overview
git diffShow unstaged line-by-line changes since last commitBefore staging / before restoring
git remote -vList the saved remotes and their addressesChecking the origin connection
git branchList branches (* marks the current one)Checking where I am

Undoing - safe recovery

CommandWhat it doesTypical usage
git restore <file>Discard uncommitted changes, back to last commitRescuing a damaged file
git restore --staged <file>Unstage a file without changing its contentStaged the wrong thing
git branch -m master mainRename the current branchFixing a repo stuck on master

Remotes - sharing with the cloud

CommandWhat it doesTypical usage
git remote add origin <ssh-url>Save a remote’s address under nickname originConnecting a local repo to GitHub
git remote set-url origin <ssh-url>Overwrite the address of an existing remoteFixing a typo’d remote
git push -u origin <branch>Publish a branch and remember the pairingFirst push of a branch
git pushSend committed work up to the remoteEvery push after the first
git pullFetch remote commits and merge into localStart of every work session
git clone <ssh-url>Download a full copy of a remote repoNew machine / new project

Branching & merging - parallel work

CommandWhat it doesTypical usage
git switch -c <name>Create a branch and move onto itStarting a new piece of work
git switch <name>Move to an existing branchChanging what’s on my desk
git merge <branch>Bring another branch’s commits into the current oneMerging locally (PR button does this remotely)

Symptom / messageWhat it meansThe fix
nothing to commit / no changes added to commitI edited but never staged - the box was emptygit add <file>, then commit again
Stuck in a strange text editor after git commitForgot -m, so Git opened vim for the messagePress Esc, type :q!, Enter - then recommit with -m "message"
Commits signed with the wrong name/emailgit config was wrong or skippedRerun the two git config --global lines; new commits carry the fix
On branch master (everyone else says main)Repo was made before init.defaultBranch was setgit branch -m master main
Permission denied (publickey)GitHub doesn’t recognise the machine (key handshake failed)Run ssh -T git@github.com; if it doesn’t greet me, re-walk the SSH key steps
remote origin already existsAn origin is already saved; nicknames are uniquegit remote -v to inspect, then git remote set-url origin <url>
Updates were rejected / fetch first (non-fast-forward)The remote has commits I haven’t pulledgit pull, then git push - the pull-first habit prevents it entirely
Committed on the wrong branchstatus was skipped; commit landed on the wrong lineIf noticed before committing: git switch -c the-branch-I-meant (uncommitted work travels with me). If already committed: move it with help - nothing is lost
There isn't anything to compare on GitHubThe branch was committed locally but never pushedgit push -u origin <branch>, then refresh the compare page
Merge conflict (CONFLICT (content))Both branches changed the same lines differentlyEdit the file to the wanted truth, delete the <<<<<<< ======= >>>>>>> markers, then git add + git commit

Must-knowOne-line recall
Git vs GitHubGit = local tool that records history; GitHub = cloud service that hosts it
Three areasWorking directory → staging area → repository (→ remote)
Why staging existsTo choose exactly what goes into each save point
First-time setupgit config --global name, email, and init.defaultBranch main - once per machine
Everyday loopedit → status → add → commit → push
Good commit messageImperative, short first line, says what and why
Commit habitOne logical change per commit; small and often
.gitignoreLists patterns Git never tracks; the file itself is committed
originThe conventional nickname for a project’s main remote
Push vs pullPush sends commits up; pull brings them down; nothing syncs by itself
SSH keyPrivate key stays home, public key given to GitHub once - no passwords over Git
-u flagNeeded once per branch’s first push; sets the remote pairing
BranchIndependent line of history; main stays stable, work happens on short-lived branches
HEAD”Where I am standing now” - the tip of the current branch
Pull requestProposes merging a branch into main and opens a review conversation
git merge grammarStand on the receiver, name the branch being brought in
Merge conflictBoth sides changed the same lines; edit to the truth, remove markers, add + commit
GitHub PagesServes a repo as a website; needs index.html; edit → commit → push → live
Every errorRead Git’s message - it names the problem and usually the fix