Git Notes

Git Notes Cover Image

Git:

  • IS NOT GITHUB
  • Is a distributed Version Control System (VCS)
  • tracks changes in specific repositories (repo)
  • helps manage versions effectively

Git Terminology

Repo:
contains remote/local project’s files and history.
Staging area:
where files ready to be committed.
commit:
snapshot of your project’s current state.
branch:
movable pointer to a commit.
Clone:
duplication of a remote repo.

Git Download:

Git is pre-installed in Mac & Linux

git --version # check if you have git

download link: https://git-scm.com/downloads

Configuration:

Set values:

git config set --global user.name "idriss"
git config set --global user.email "[email protected]"   
git config set --global core.editor "code" # the default editor git will use for commit messages, etc. (vim, nano, ...)

Get values:

git config list # list all configurations
git config get user.name

Delete values:

git config unset user.name # delete a variable

Editing the Config file Directly

git config edit --global # opens the default editor (core.editor) to edit the config file directly, it's located on `.git/config` for local config, and on `~/.gitconfig` or `C:\\Users\\YourName\\.gitconfig` for global config.

Git Workflow:

1. Initialize a Repo

git init # initialize a new local repo
git clone <remote-repo-url> # clone an existing remote repo

git will create a hidden folder named .git; deleting it will uninitialize the repo.

2. Make Changes

  • create files: touch index.html script.js test.txt
  • edit files: echo console.log("hello git!") > script.js
  • delete files: rm test.txt

3. Stage Changes:

git add <file> [...<file>] # to specify which files to stage
git add . # to stage all files in the current repository

4. commit changes:

take a snapshot of the current staged files

git commit -m "message"
git log # to track commit history

5. push Changes:

to sync the remote repo with your local commits

git remote add origin <remote-repo-url> # point origin to the remote repo url, like alias so we don't write the entire url ever time
git push --set-upstream origin <branch> # or:
git push -u origin <branch-name>

Branches:

A branch is a movable pointer to a specific commit in the repository

git branch # list all local branches
git branch -avv # list all branches
git branch <branch-name> # create branch
git switch -c <branch-name> # create and switch to branch
git checkout -b <branch-name> # same as above (old syntax)

Merging

Bringing two branches back into sync by combining changes from one into another.

  • The branch you’re on is the branch you’re bringing other changes into
git merge <target-branch-name>
git branch -d <branch-name> # delete a branch
  • This doesn’t delete any commits; it just deletes the branch “label” so you can’t use it any longer. You can still use all the commits.
  • A topic branch is what we call a local branch made for a single topic, like a feature, bug fix, etc.
git branch -D topic1
  • forcefully delete a specified branch in Git, even if it contains unmerged changes

Git Resetting

Undo previous changes in the local repository (rewrites history)

Reset types:

  • soft: Moves HEAD to the target commit but keeps all changes staged -> “ready to commit”
  • mixed: (DEFAULT) Moves HEAD to the target commit and unstages the changes -> “modified”
  • hard: Moves HEAD to the target commit and discards all changes; all the changes will be gone.
git reset --soft <commit-hash> # resetting commit while keeping files staged
git reset --mixed <commit-hash> # default
git reset <commit-hash> # unstages changes after the specified commit
git reset --hard <commit-hash> # discard changes after specified commit

Git Revert:

similar to reset but keeps the log (doesn’t change history) and creates a new commit.

git revert <commit-hash> [...<commit-hash>] # reverting one or multiple commits with a revert commit for each one

—no-commit option prevents auto-commit

git revert -n <commit-hash> 
git revert <oldest-commit>^..<newest-commit> # reverting a range of commits
git revert --continue # after fixing merge conflict (similar to rebase)

Going Back In Time

The HEAD is a pointer to the current state of your repository. By default, it references a branch, but it can also point directly to a commit in a detached state.

git switch --detach <commit-hash>

Reattach HEAD to the main branch. There are two options:

git switch - # this switches to wherever we were before this, which, in this case, was main.
git switch main # this explicitly switches to main.
git checkout main # using the old checkout syntax

Commits Relative to HEAD

To return to the previous commit:

git switch --detach HEAD^
git switch --detach @^  # @ is the same as HEAD

To return to the 3th previous commit

git switch --detach HEAD^^^
git switch --detach HEAD~3

Pushing

git push <remote-alias> <remote-branch>

Remote tracking

git remote -v # list all remotes

change remote URL

git remote set-url <remote-name> <url>

Add a remote

git remote add <remote-name> <url>

Set upstream branch (links local branch to remote branch):

git push --set-upstream origin main
git push -u origin main              # same thing, shorthand

After setting upstream, you can simply run:

git push

Clean up remote tracking branches that no longer exist on remote:

git fetch --prune
git fetch --prune <some-remote> # prune specific remote
git fetch --prune --all # prune all remotes

Delete local remote-tracking branch:

git branch -dr <remote>/<branch> # -d delete -r remote

Delete branch fom remote

git push <some-remote> --delete <branch-name>

file State

git status
  • Untracked: Git doesn’t know anything about this file ($ git add <file-name> for staging OR add it in .gitignore to ignore it)
  • Unmodified
  • Modified
  • Staged: The files ready to be committed (git commit OR git restore --staged : remove from stage and go back to modified state)

.gitignore

a text file that lists all files and directories that should be ignored by Git.

Syntax and Rules:

  • Lines starting with # are comments
  • ! prefix negates the pattern, re-including any matching file excluded by a previous pattern
  • Wildcards are supported (*, ?, [])
  • Trailing / specifies a directory
  • Leading / makes the pattern relative to the repository root

Examples:

*.tmp
*.sw[op]

# above is the same as

*.tmp
*.swo
*.swp

Ignore generated HTML files, except foo.html:

*.html
!foo.html

Common patterns:

# Ignore all files in a directory
node_modules/

# Ignore specific file in root only
/config.local

# Ignore pattern anywhere in the tree
**/*.log

# Ignore all .txt files in doc/ directory and subdirectories
doc/**/*.txt
GitHub list of .gitignore files

Diffing

Shows the difference between two files or commits.

git diff  # compares working tree and the index (staging area)
git diff --staged #  diff the stage with the previous commit
git diff <commit-hash> <commit-hash> # compare two commits
git diff HEAD~4 HEAD~3
git diff HEAD~3^!          # Same thing (^! means "this commit vs its parent")

formatting:

git diff -U5 # shows 5 lines of context around the changes (default is 3)
git diff --name-only # only list the names of changed files

$ git diff -w #  dealing with formatting inconsistencies
$ git diff --ignore-all-space # Same thing

Diff specific files:

git diff -- <file> [...] # diff specific files
git diff <branch-or-commit> -- <file> [...]
git diff <branch-or-commit> <branch-or-commit> -- <file> [...] # files between commits
git diff *.py # diff files that match a pattern
A---B---C---D  (branch1)
      \
        E---F---G  (branch2)
git diff branch1...branch2 # everything that branch2 has added since diverging == git diff B branch2

Renaming

Using regular filesystem commands:

mv old.txt new.txt
  • Git has no idea you did this until you git add -A (stage changes).
  • Git sees “file deleted” + “new file created”.

Using Git’s rename command:

git mv old.txt new.txt
  • the filesystem rename and stages it as a rename in Git’s index in one step.
  • Git knows explicitly: “this file was renamed.”

Renaming the file back to its old name will undo the change:

git mv new.txt old.txt

Removing

git rm file.txt
  • Removes the file from the working directory and stages the deletion immediately.

To restore uncommitted removed files

  1. git restore --staged file.txt file goes from staged to modified state (still not shown in directory)
  2. git restore file.txt file goes to unmodified state (now it’s undeleted)

To restore deleted files from an old commit

1. find the target commit

git log -- <file-name> # lists all commits where that file has changed

git log --name-only # if you forget the file name

2. restore the file

git restore --source=<commit-hash> <file-name>

Rebasing

never rebase anything that you have pushed

allows developers to integrate changes from one branch into another. It provides a cleaner, more linear project history compared to merging, making it easier to track changes and understand the evolution of a codebase.

A---B  (main)
  \
    E---F  (topic)

Running $ git rebase main while on topic branch will change history to be like this:

A---B  (main) ---E'---F'  (topic)

running $ git pull results in the following:

git fetch			     # Get all the information from origin
git merge origin/main    # Merge origin/main into main

to rebase instead of merge while pulling:

git pull --rebase

or set it to be the default:

git config set --global pull.rebase true

Conflicts in Rebase:

to resolve a rebase conflict:

  1. Edit the conflicting file and make it Right.
  2. Stage it. (git add .)
  3. Continue the rebase. (git rebase --continue)

Interactive rebasing

Allows for interactive modification to the commit history (squashing, editing, reordering)

git rebase -i <base>  # <base> is the commit before the range you want to modify

Git stash

Temporarily save uncommitted changes without committing them (WIP feature)

git stash # push the changes to the stash stack
git stash list # show the items in the stash
git stash pop # applies the last changes from the stash stack and removes it from the stack.
git stash pop stash@{0} # the same as above
git stash apply <stash> # applies stash changes while keeping them in the stack
  • <stash>: e.g., stash@1 or —index 1
git stash drop [<stash>] # pops and discards the latest stashed changes if no stash is specified
  • You have to stage the changed files ( git add ) before stashing them.
  • The top of the stack is stash@{0}
  • In a conflict case, the stashed changes will still be in the stash!

Reference log (reflog)

reflog tracks the steps behind every change (beyond git log); it’s useful when recovering from a bad reset.

git reflog # get the hash of your lost commit
git branch <new-branch-name> <commit-hash>  # create a new branch of that commit to recover it

Patch Mode: Partial changes

Patch mode allows you to select which hunks (a collection of close changes) will be operated on.

Commands that use -p include add,reset, stash, restore, commit, and more.

git add -p # will ask which hunk to stage and which to ignore
git reset -p <commit-hash> # This is not a hard, soft, or mixed reset!

Cherry-Picking

Cherry-picking means choosing a commit from one branch and applying it to another.

git cherry-pick <commit-hash> # from the destination branch
git cherry-pick --continue # after resolving cherry-pick conflicts

Blame

Blame shows you who made specific changes, so you know who to blame!

git blame [options] <file-name>

useful options:

  • -L <start>,<end>: specify the line range
  • -M: Detect moved or copied lines within the same file, reporting the original author of the lines instead of the last author that moved or copied the lines
  • -C: Detect lines that were moved or copied from other files.
  • —date=short: show only the day of modification without the time
  • —color-lines: alternating colors between commits. customize the color with git config set --global color.blame.repeatedLines <color>
  • —show-email or -e: shows the contributor’s email address instead of the username

Git Alias

Aliases are used to map repetitive and long commands to shorter ones.

git config set --global alias.<alias-name> <git-command> # define a new alias
git config --get-regexp alias # get all current aliases

Useful Aliases:

git config set --global alias.uncommit "git reset HEAD~1"

git config set --global alias.recommit "git commit --amend --no-edit"

git config set --global alias.editcommit "git commit --amend"

git config set --global alias.logc "log --oneline --graph --decorate"

Amending Commits

Amend allows you to modify the recent commit. It’s useful for:

  • Editing or fixing typos in commit messages
  • Adding files from the last commit without creating a new commit

Amend commit message:

git commit --amend # opens an editor to change the last commit message
git commit --amend -m "<new-message>" # changes the last commit message directly

Add files to the last commit:

git add <file> # Stage the missing file
git commit --amend --no-edit # Amend the commit without changing the commit message

Tagging

Tags are used to mark particular commits:

  • Lightweight: Just a tag, e.g., v3.14.
  • Annotated: lightweight tag + message + author.

Creating tags:

git tag # list all tags
git tag v1.0 # creates a lightweight tag in the current commit
git tag v1.0 <commit-hash> # specify a commit to tag
git tag -a 1.1 -m "tag message" # create an annotated tag

Pushing tags:

git push [origin] --tags # push all tags
git push origin tag3.14 # specify which tag to push

Deleting tags:

git tag -d <tag-name> # delete tag locally
git push origin -d <tag-name> # delete from the remote

Submodules

Git submodules allow you to have nested modules by putting one remote repository inside another repo.

Cloning a repo with submodules:

git clone --recurse-submodules <repository-url>

If you forgot to use --recurse-submodules during cloning, use:

git submodule update --init --recursive

Creating a submodule:

git submodule add <remote-repo-url>

Config

Recommended settings to allow Auto-update submodules when pulling and pushing

git config set --global submodule.recurse true

git config set --global push.recurseSubmodules on-demand

Deleting a Submodule

git submodule deinit <mysubmod>
rm -rf .git/modules/<mysubmod>
git config -f .gitmodules --remove-section submodule.<mysubmod>
git rm --cached <mysubmod>

Worktrees

worktrees allow you to work on multiple branches simultaneously without constantly stashing and switching branches.

Worktree Rules:

  • No two worktrees may refer to the same branch at the same time (except when using detached HEAD).
  • Place the worktree as a sibling directory of the existing worktree for better organization.
  • There is only one main working tree that contains the .git directory; additional worktrees link to it.

Commands

git worktree add ../<worktree-name> <branch-name> # create a new worktree in the parent directory with the specified branch
  • If <branch-name> is omitted, the basename of <path> will be used as the branch name. If no branch exists with that name, a new branch will be created.
git worktree remove <worktree-path> # removes the working tree (can be run from any location).
  • <worktree-path> can be any path that leads to the worktree, including . if run from within the worktree itself.
git worktree list # list all worktrees
  • main working tree is listed first.

Pro tips

  • Only change history if you haven’t pushed the changes yet! Rewriting shared history causes problems for collaborators.
  • Git doesn’t track empty directories. Workaround: add a .gitkeep file (or any file) to the directory so Git tracks it.
  • Upstream remote naming convention: When forking a repo on GitHub, the convention is to name the original repo’s remote upstream and your fork origin.
  • Safer force push: Use git push --force-with-lease instead of --force. It only pushes if the remote branch hasn’t been updated since you last fetched (prevents overwriting others’ work).
  • You can move your uncommitted changes to a new branch with git switch -c <new-branch>
  • Commit message conventions: Use imperative mood (“Add feature” not “Added feature”). Consider using Conventional Commits for a structured format (e.g., feat:, fix:, docs:).
  • .gitattributes file is a special file that tells Git how to handle specific files in your repository
  *.jpg binary           # treat as binary
  *.sh text eol=lf      # force LF line endings
  docs/* linguist-documentation  # exclude from language stats

Resources:

Share this post