Git Notes

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.
- the local config override the global config!
- git has a deprecated syntax for the config: https://git-scm.com/docs/git-config#\_deprecated\_modes
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>
A merge into a direct ancestor is a fast-forward and will not cause a merge conflict.
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
Merge result is a new commit called a merge commit that has two predecessors; it can be automatic or manual if a merge conflict happens.
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>
at least 4 unique digits of the commit ID are required ($ git log --oneline
to see short hashes)
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
ORgit 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
the order of commits matters; goes from first commit to second commit.
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
git restore --staged file.txt
file goes from staged to modified state (still not shown in directory)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:
- Edit the conflicting file and make it Right.
- Stage it. (
git add .
) - 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
Tags can’t be deleted from other people’s clones!
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>
- You can not make a submodule out of a local repo!
- Using a package manager (e.g., npm, pip, etc…) is better alternative to submodules
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 forkorigin
. - 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