Git Essentials & Setup

Initial Configuration

Set your user identity for all local repositories. Typically done once per machine.

  • Set user name: git config --global user.name "Your Name"
  • Set user email: git config --global user.email "[email protected]"
  • Set default branch name for new repos (recommended "main"): git config --global init.defaultBranch main
  • (Optional) Set your preferred text editor: git config --global core.editor "code --wait" (Example for VS Code)
  • Check your configuration: git config --list
Getting a Repository

Start a new project with Git or obtain a copy of an existing one.

Initialize a new local repository:

Navigate to your project directory, then run: git init

Clone an existing repository:

git clone <repository_url>

Example: git clone https://github.com/example/project.git

This creates a local copy named after the project.

The Three Main States

Understand Git's core areas: Working Directory, Staging Area (Index), and the Repository itself.

Working Directory

Your project files

Staging Area (Index)

Changes for next commit

Repository (.git)

Committed history

  • Working Directory: The files you actively modify in your project.
  • Staging Area (Index): A "holding area" where you prepare (stage) changes before committing them. Use git add to stage.
  • Repository (.git directory): Where Git stores the complete history of your project (commits, branches, tags). Use git commit to save staged changes here.
Ignoring Files (.gitignore)

Specify intentionally untracked files or directories that Git should ignore (e.g., build artifacts, log files, environment variables).

Create a file named .gitignore in your project's root directory.

Each line in .gitignore specifies a pattern:

  • Blank lines or lines starting with # are treated as comments.
  • Standard glob patterns are used (e.g., *.log, node_modules/, build/).
  • A leading slash / matches files only in the root directory.
  • A trailing slash / specifies a directory.
  • Prefix with ! to negate a pattern (un-ignore) if a parent folder is ignored.
Example .gitignore:
# Comments start with a hash
# Ignore compiled Python files
*.pyc
__pycache__/

# Ignore dependency directories
node_modules/
vendor/

# Ignore sensitive files
.env
*.secret

# Ignore OS-generated files
.DS_Store
Thumbs.db

It's crucial to commit your .gitignore file to the repository so it's shared with collaborators.

For common templates, check GitHub's .gitignore collection.

Core Operations: Recording & Viewing Changes

Staging & Committing

The fundamental workflow: check status, stage changes, and commit them to history.

Check Status:

git status (shows current state of working directory and staging area)

Staging Changes (Adding to Index):
  • Stage a specific file: git add <filename>
  • Stage multiple files: git add <file1> <file2>
  • Stage all changes in current directory and subdirectories: git add .
  • Stage all tracked files (new, modified, deleted) project-wide: git add -A
  • Interactively stage parts of files: git add -p (Patch mode)
Committing Staged Changes:
  • Commit with a message: git commit -m "Your descriptive commit message"
  • Commit, opening editor for message: git commit
  • Stage all modified or deleted tracked files and commit: git commit -a -m "Message" (Does NOT add new untracked files)
  • Modify the last commit (if not yet pushed): git commit --amend (Opens editor for message, adds currently staged changes) Rewrites history!
Viewing History & Diffs

Inspect past commits and see differences between states.

Viewing Commit History (Log):
  • Show full history: git log
  • Condensed one-line view: git log --oneline
  • With graph, decoration, and all branches: git log --graph --decorate --oneline --all
  • Limit number of commits: git log -n 5 (shows last 5)
  • Show changes (patches) for each commit: git log -p
Viewing Differences (Diff):
  • Changes in working directory not yet staged: git diff
  • Changes staged but not yet committed: git diff --staged (or --cached)
  • Changes in working directory since last commit: git diff HEAD
  • Differences between two commits: git diff <commit1> <commit2>
  • Differences between two branches: git diff <branch1>..<branch2>
Removing & Moving Files

Manage files within Git's tracking system.

Removing Files:
  • Remove from working directory & staging: git rm <filename>
  • Remove from staging only (keep file in working directory): git rm --cached <filename>
Moving or Renaming Files:

git mv <old-name> <new-name>

Equivalent to manually renaming, then git rm <old-name> and git add <new-name>.

Branching & Merging Strategies

Branch Management

Isolate development work using branches. Create, list, switch, and delete them.

Branches are lightweight movable pointers to commits, allowing parallel lines of development.

  • List local branches: git branch (* indicates current branch)
  • Create a new branch (does not switch): git branch <branch-name>
  • Switch to an existing branch: git switch <branch-name> (Modern Git)
    (Older: git checkout <branch-name>)
  • Create a new branch and switch to it: git switch -c <branch-name> (Modern Git)
    (Older: git checkout -b <branch-name>)
  • Delete a merged local branch: git branch -d <branch-name>
  • Force delete an unmerged local branch: git branch -D <branch-name> Use with caution!
  • Rename current branch: git branch -m <new-name>
Simplified Branching Visual

A basic illustration of creating and merging a feature branch.

M1
M2
F1
M3
M=Main branch commits, F=Feature branch commit.
Merging & Rebasing

Combine work from different branches using merge (preserves history) or rebase (linearizes history).

Merging:

To merge feature-branch into your current branch (e.g., main):

  1. Switch to target: git switch main
  2. Merge: git merge feature-branch

May result in a "merge commit" if histories diverged.

Resolving Merge Conflicts:

If Git reports conflicts:

  1. Open conflicted files (look for <<<<<<<, =======, >>>>>>>).
  2. Edit files to resolve, then save.
  3. Stage resolved files: git add <resolved-file>
  4. Commit the merge: git commit
Abort merge: git merge --abort (before committing).


Rebasing (Alternative to Merge):

To rebase feature branch onto main (reapplies feature commits on top of main's tip):

  1. Ensure main is up-to-date: git switch main; git pull (if remote)
  2. Switch to feature branch: git switch feature
  3. Rebase: git rebase main

Warning: Do NOT rebase commits that have already been pushed to a shared remote repository and are used by others. It rewrites history and can cause severe problems for collaborators.

Conflicts during rebase: Resolve, git add <file>, then git rebase --continue. Abort: git rebase --abort.

Gitflow Workflow: A Common Branching Model

Gitflow is a structured branching model designed for projects with scheduled releases. It defines specific roles for different branches. Click branch names below for details.

Hotfix Branches
Main (Master)
Release Branches
Develop Branch
Feature Branches
Main (Master) Branch

Purpose: Stores the official release history. Code on main should always be stable and deployable. Each commit on main is a tagged release (e.g., v1.0, v1.0.1).
Derives from: Initial commit.
Merges into: Nothing (but receives merges from release and hotfix branches).

Develop Branch

Purpose: Serves as an integration branch for features. Contains the latest development changes for the next release. This branch can be unstable at times.
Derives from: main.
Merges into: Nothing directly (but feature branches merge into it, and release branches are created from it).

Feature Branches (e.g., feature/user-auth)

Purpose: Used to develop new features. Each feature gets its own branch to isolate work.
Derives from: develop.
Merges into: develop (once the feature is complete and reviewed).
Naming: Typically feature/* or feat/*. Should not interact directly with main.

Release Branches (e.g., release/v1.1.0)

Purpose: Prepare for a new production release. Allows for final bug fixes, documentation generation, and other release-oriented tasks. No new major features are added here.
Derives from: develop (when develop is deemed feature-complete for the release).
Merges into: main (tagged with release version) AND back into develop (to ensure fixes are in future development).

Hotfix Branches (e.g., hotfix/critical-login-bug)

Purpose: Address urgent critical bugs in a production release quickly. Allows patching without disrupting ongoing development on develop.
Derives from: main (from the specific tagged version that has the bug).
Merges into: main (tagged with a new patch version) AND back into develop (to ensure the fix is also in subsequent development).

Gitflow is one of many branching strategies. Others include GitHub Flow (simpler, for continuous deployment) and GitLab Flow.

Remote Collaboration & Synchronization

Managing Remotes

Connect your local repository to remote ones (e.g., on GitHub, GitLab, Bitbucket).

  • List remotes with URLs: git remote -v
  • Add a new remote: git remote add <name> <url> (Common name: origin)
  • Show information about a remote: git remote show <name>
  • Change a remote's URL: git remote set-url <name> <new-url>
  • Rename a remote: git remote rename <old-name> <new-name>
  • Remove a remote: git remote remove <name>
Fetching & Pulling Changes

Download changes from a remote repository to your local one.

Fetch (Download, Does NOT Merge):

git fetch <remote-name> (e.g., git fetch origin)

Downloads commits, files, and refs from the remote into your local repository. Updates remote-tracking branches (e.g., origin/main). Does NOT automatically merge these changes into your local working branches.

After fetching, you can inspect changes (e.g., git log origin/main..main) or merge manually (git merge origin/main).

Pull (Fetch + Merge):

git pull <remote-name> <branch-name> (e.g., git pull origin main)

Effectively runs git fetch followed by git merge <remote-name>/<branch-name> into your current local branch. If your local branch tracks a remote branch, a simple git pull often suffices.

Pushing Changes

Upload your local commits to a remote repository to share them.

git push <remote-name> <branch-name> (e.g., git push origin main)

Transfers commits from your local branch to the specified branch on the remote.

Setting an Upstream Branch:

The first time you push a new local branch, you may need to set its upstream counterpart on the remote:

git push -u <remote-name> <local-branch-name>

This sets <remote-name>/<local-branch-name> as the upstream. Subsequent pushes from this local branch can use a simple git push.

Force Push: git push --force <remote> <branch> (or -f). Overwrites the remote branch. Can discard history and cause problems for collaborators. Use with extreme caution and only if you know what you're doing (e.g., on a personal feature branch you haven't shared).

Working with Remote Branches

Understand and manage references to branches on your remotes.

Remote-tracking branches are local read-only references to the state of branches on your remote repositories (e.g., origin/main, origin/feature-x). You don't work on them directly.

  • List remote-tracking branches: git branch -r
  • List all branches (local and remote-tracking): git branch -a
  • Create a new local branch that tracks a remote branch:
    git switch -c <local-branch> <remote>/<remote-branch>
    (e.g., git switch -c my-feature origin/feature-abc)
    Often, if feature-abc only exists on one remote, git switch feature-abc will automatically set up tracking.
  • Delete a remote branch: git push <remote-name> --delete <branch-name>
    (e.g., git push origin --delete old-feature)

Advanced Tools & History Management

Tagging Commits

Create permanent markers for specific commits, typically used for software releases (e.g., v1.0, v2.1.3).

  • List all tags: git tag
  • Create a lightweight tag (just a pointer, no extra info): git tag <tag-name>
  • Create an annotated tag (recommended for releases; stores extra metadata like tagger, date, message): git tag -a <tag-name> -m "Tag message for version X.Y.Z"
  • Tag an older commit specifically: git tag -a <tag-name> <commit-hash> -m "Message"
  • Push a single tag to remote: git push <remote-name> <tag-name> (e.g. git push origin v1.0)
  • Push all local tags to remote: git push <remote-name> --tags
  • Delete a local tag: git tag -d <tag-name>
  • Delete a remote tag: git push <remote-name> --delete <tag-name>
Stashing & Cleaning

Temporarily save uncommitted changes (stash) or remove untracked files from your working directory (clean).

Stashing (Temporarily Saving Changes):

Useful when you need to switch branches but have uncommitted work.

  • Stash current uncommitted changes (tracked files): git stash or git stash push -m "Optional message"
  • Stash including untracked files: git stash -u (or --include-untracked)
  • List stashes: git stash list
  • Apply the most recent stash and remove it from stash list: git stash pop
  • Apply the most recent stash but keep it in stash list: git stash apply
  • Apply a specific stash: git stash apply stash@{N} (N is stash index)
  • Drop the most recent stash: git stash drop
  • Clear all stashes: git stash clear Irreversible!
Cleaning (Removing Untracked Files):

Warning: git clean permanently deletes files from your working directory. Use with extreme caution.

  • Dry run (see what would be deleted): git clean -n (or --dry-run)
  • Remove untracked files: git clean -f (--force)
  • Remove untracked directories as well: git clean -fd
  • Interactive cleaning: git clean -i
Resetting & Restoring

Powerful commands to undo changes at different levels. Understand their impact before use.

git reset (Moves HEAD, affects Index/Working Dir based on mode):

Primarily used to unstage changes or revert commits on a local branch before pushing.

  • git reset --soft <commit>: Moves HEAD to <commit>. Staging area and working directory are NOT changed. Commits after <commit> become staged changes. Useful for redoing a series of commits.
  • git reset --mixed <commit> (Default mode): Moves HEAD to <commit>. Staging area is updated to match <commit>. Working directory is NOT changed. Changes are now unstaged. Example: git reset HEAD~1 (uncommits last commit, changes are unstaged).
  • git reset --hard <commit>: Moves HEAD to <commit>. Staging area AND working directory are forcefully updated to match <commit>. Warning: Discards ALL uncommitted changes in staging and working directory since <commit>. Potentially destructive and can lead to data loss if not used carefully.
git restore (Restores files in Working Dir or Staging Area):

A safer, more focused command for discarding changes.

  • Discard changes in a working directory file (revert to last commit): git restore <filename>
  • Unstage a file (remove from staging area, keep changes in working dir): git restore --staged <filename>
  • Discard changes and unstage from a specific commit: git restore --source=<commit> --staged --worktree <filename>
Revision Selection (Referring to Commits)

Various ways to specify particular commits in Git commands (log, diff, reset, etc.).

  • Full SHA-1 hash (e.g., 1a410efbd13591db07496601ebc7a059dd55cfe9)
  • Short SHA-1 hash (e.g., 1a410e - usually first 7 chars are enough)
  • Branch names (e.g., main, feature/login - points to the tip of the branch)
  • Tag names (e.g., v1.0.0 - points to the tagged commit)
  • HEAD: Points to the currently checked-out commit (tip of current branch).
  • HEAD~1 or HEAD^: The first parent of HEAD.
  • HEAD~N: The Nth grandparent of HEAD (following first parents).
  • HEAD^N: The Nth parent of HEAD (useful for merge commits with multiple parents).
  • <branch>@{yesterday} or <branch>@{"2 hours ago"}: Commit on a branch at a specific time.
  • <branch>@{N}: Nth prior position of the branch reference (uses the reflog).
  • :/<text>: Newest commit whose message contains <text>.
Copied to clipboard!