Linux Maxxing dot com

Git Notes

Git Repository

Concepts

File States

  1. untracked
  2. staged
  3. committed

Commit

  • snapshot of the repository at a given time
  • it is not a diff
  • diffs are calculated

Commits are just a file

  • first two hash characters = the directory within .git/objects
  • the rest is the filename
  • this is to prevent inode busting from putting all commits under one directory
  • commit stores entire history via pointers

Hash

  • unique; depends on every aspect of the git operation:

    • author name and email
    • commit
    • message
    • date and time
    • parent commit hashes

Git Objects

  • git is made of up objects in the .git/objects directory
  • a commit is just one type of these objects
  • these objects will match commits shown in git log
  • cat will show binary nonsense
  • git log will output the object in hexadecimal format
  • git compresses files in the .git/objects directory
  • git deduplicates files common across commits
  • git does not change or overwrite past trees or blobs
  git init                        # create git repo in current directory
  git status                      # see file states
  git add .                       # does not recursively add

  git log
  git --no-pager log
  git --no-pager log -n 10    # prints last 10 commits NOT in the pager (less)

  # other options
  --oneline # puts everything on one line
  --parents # shows parent commmits
  --graph   # shows commit/branch lines
  --all    # shows all branches
  --decorate # branch + tag info, can be short, full,no

  git log --decorate=full


git internals

  • tree: git's way of storing a directory
  • blob: git's way of storing a file
  # use the first 7 characters of the commit hash (4 might be sufficient)
  git cat-file -p <commit hash>   # this will show the tree hash
  git cat-file -p <tree hash>     # this will show the blob hash
  git cat-file -p <blob>          # this will show the file hash
  git cat-file -p <file hash>     # shows contents of the file

git config

You can store any data you want in your git config files

  • git might not use the data, but it can live there
  • use –global and –local flags after the operation
  • git config files can also be directly edited
  • git is case insensitive; defaultBranch and defaultbranch are the same

Config files are located in the following places in reverse priority:

Location Config File
System /etc/gitconfig
Global ~/.gitconfig
Local .git/config
Worktree .git/config.worktree

Local definitions will take priority over global definitions.

git config set

  git config set --global user.name "Bill S. Preston, Esq."

  # useless data for git, but allowed
  git config set --local organization.ceo "Ted"

  # --add functionally does the same thing
  git config --add --global user.email "bill@email.com"

git config list

  git config list --local
  git config list --global
  cat .git/config                 # local data
  cat ~/.gitconfig                # global data

git config get

  git config get <key>              # <key> made up of section.value
  git config get user.name          # section = user, value = name
  git config get --global user.name # --global goes after get/set

git config unset

  git config unset <key>           # <key> needs a section and value
  git config unset --all <section> # unset all of the same section
  git config unset user.name
  git config unset user            # THIS FAILS - needs a key and value

duplicates

  # you can have multiples of a key
  # the last one is what is tracked in git
  git config set --append organization.ceo "Warren"
  git config set --append organization.ceo "Carson"
  git config set --append organization.ceo "Sarah"

  # unset all of the same key
  # otherwise git will not allow unsetting that key
  git config unset --all <key>

Remove a section

  git config remove-section <section>
  git config remove-section user


Branch

  • Branch is a named pointer to a specific commit.
  • When you create 10 branches, you are not creating 10 copies of the project.
  git branch                      # shows you which branch you are on
  git branch -m oldname newname   # rename
  git config set --global init.defaultBranch main

Create a branch

  git branch <branchname>
  git switch -c <branchname>

  #   Create a new branch off of a specific commit hash
  git switch -c <branchname> <commit hash>

  git checkout <branchname>          # only works if <branchname> exits

  git branch -d <branchname>        # delete branch
  git branch -D <branchname>        # force delete a branch (unmerged commits)

When you create a branch:

  • git uses the current commit you are on as the branch base
  • HEAD moves to that branch

After deleting a branch

  • The commits from that branch will no longer be visible in git log
  • Even with git log --all, you will no longer see any unmerged commits from that branch
  • Commits will still be visible in reflog

HEAD and tip

tip = most recent commit in a branch

HEAD = commit you are working off of

HEAD usually will be the tip, but does not necessarily have to be.

The commit hash of HEAD of a branch is stored in .git/refs/heads

HEAD is stored in a file:

  • .git/HEAD
  • This points to the branch HEAD stored in .git/refs/heads


Merge

Typical Workflow

  • Fetch or pull updated main branch
  • Create a branch to do stuff
  • Merge branch to main when sufficient via pull request or git merge on cli

Merge Commit

  • Commit which merges two branches with diverging histories.
  • Only kind of commit with two parent commits.
  • Relative to Merge Base, aka best common ancestor
  git merge <feature_branch_name>            # usually done from main branch

Fast-forward merge

  • Simplest
  • Tip of feature branch becomes the tip of main branch
  • NO MERGE COMMITS
  • Feature branch is merged into the main branch prior to additional main branch commits


Rebase

Most misunderstood features of git

  • Potential drawbacks for the unintiated:
  • e.g. someone changes the public history of a branch which screws up a ton of stuff

Ideal scenario:

  • You are working on a feature branch
  • There have been updates to main
  • You want to pull those updates from main onto your feature branch
  • After rebasing, this allows for a fast-forward merge

Said otherwise

  • Push the parent commit of the feature branch to the new tip of the main branch
  • The tip of the feature branch is now a fast-forward merge away from the main branch

Allows maintaining a merge-commit-free history

  • Has some benefits
  • Allows using certain git features if you maintain your history this way

When on the feature branch, rebase with main

  git rebase main                # from the feature branch

WARNING: NEVER REBASE A PUBLIC BRANCH LIKE MAIN OR MASTER

  • Out of habit, it is a good idea not to rebase your own private/fork main branches

Rebase vs Merge

  • Rebase will change commit hashes
  • Will have to address conflicts if rebasing:

    • If there are conflicts in rebasing there would be conflicts in merging as well
    • Merging does not have any benefits over rebasing in this regard
    • Generally if there are no conflicts in a merge, there will be no conflicts in rebase
  • Rebase advantage:

    • Reverting commits
    • Only really done at scale
  • Merge advantage:

    • Shows true history
  • Merge vs rebase might be enforced by some teams on the main branch

    • For your own branches you can generally do whatever you want.
  • An opinionated strategy:

    • Always rebase
    • All merges then become fast-forward merges
    • If you are in a situation where you need to revert, it becomes really easy to do.
    • Squashing becomes easy

With merge conflict resolution

  • you COMMIT the resolution

      git add <file>
      git commit [-m "Message"]

With rebase conflict resolution

  • you CONTINUE the resolution

      git rebase --continue
  • you DO NOT commit rebase resolution

If you happen to accidentally commit rebase resolutiton

  • undo the commit

      git reset --soft HEAD~1
  • then continue the rebase


Undoing Changes

Git fundamentally does 3 things

  1. Add
  2. Delete
  3. Modify

Conflicts arise on the modify portion

  • If two commits edit the same line, git will throw a conflict
  • Requires personal editing and deconfliction
  • Comments in code can cause conflict

git reset –soft

  git reset --soft <commit hash>
  git reset --soft HEAD~1         # go back one commit from HEAD
  • Undo the last commit and committed changes in the index (staged but not committed)
  • Commit changes will be uncommited and staged
  • Uncommited changes will remain staged or unstaged as before

git reset –hard

  git reset --hard <commit hash>
  • Makes working directory and staging area match the last commit exactly
  • Disregards local changes

DANGER: GIT RESET –HARD IS PERMANENT

  • If we simply delete a commited/tracked file using rm -rf filename, we can easily restore
  • If we use hard reset prior to committing that file, those changes are gone for good
  • Still recoverable through reflog

Change Commit Message

Rather than soft resetting to change a commit message, you can just do this:

  # this is a destructive item because it changes the commit hash
  # only do for latest commit which is not merged to main
  git commit --amend

This is a destructive operation and will change the commit hash.



Git Remote

Remotes are just git repos

  • We can have remotes that reside on our local machine.
  • Origin: If treating the remote as the authoritative source of truth, name it "origin"
  • Upstream:

    • Sometimes this will be the authoritative.
    • Origin becomes what you're working in (fork of upstream).
    • You only want upstream when working with a lot of people and/or forking, etc.

git fetch

  • This fetches objects and bookeeping information about a repo into .git/objects
  • It does not pull the files into your local branch(es).

Remote Commands

  git remote add <remote> <uri>   # notice this is uri, can be a file address
  git remote add origin ssh://git@forge.site/user/repo.git

  git ls-remote

  git push origin main            # push the local main branch to origin

  # push a local branch to a remote branch of a differnet name
  git push origin <localbranch>:<remotebranch>

  git push origin :<remotebranch> # deletes a remote branch

  git log origin/master --oneline

  git merge <remote>/<branchname>
  git merge origin/main           # merge from the main branch on origin


Git Pull

Pulls actual filechanges onto your local branch, not just metadata.

  # ensures we merge on a pull, may or may not want this
  git config set pull.rebase false

  git pull origin main            # pulls the main branch from origin

Workflow Example:

  • Remote is truth
  • Rebase by default

      git config set --global pull.rebase true

Solo work:

  • Everything on a single branch (main)

Team work

  • Work on new-branch
  • Push to origin new-branch
  • Open PR
  • Ask for review
  • After review, hit merge on remote forge
  • Delete new-branch merging


Gitignore

Example: Adding node-modules to .gitignore will ignore every file that has exactly node-modules in its name. It will not ignore files or directories that contain node-modules-2

Exclude file

.git/info/exclude

  • Alternative to .gitignore
  • Allows your personal ignored files to not be part of the repo
  • Team members cannot see what kind of shady stuff you're hiding from the repo

Nested .gitignore

  • .gitignore does not have to be a t the project root
  • There can be .gitignore in any directory of the project

Pattern Recognition

Patterns starting with a forward slash are anchored to that directory

  • /main.py would ignore main.py in the root directory but not any other directories
  • "*" matches any number of characters except forward-slash
  • "#" comments
  • "!" indicates negation: negate specific files that would otherwise be ignored

      ,*.txt
      !/important.txt                 # negates important.txt despite the *.txt wildcard

Order matters:

  • one entry can override another
  • correct:

      temp/*
      !temp/instructions.md      # this ignores all temp/ files except for instructions.md
  • incorrect:

      !temp/instructions.md
      temp/*                          # this overrides the above negation

What to ignore:

Ignore things that can be generated or sensitive data

  • compiled code
  • minified files
  • dependencies (e.g. node_modules, venv, packages, etc.)
  • personal or specific preferences (e.g. editor settings)
  • sensitive or dangerous things (e.g. .env files, passwords, API keys, etc.)


Fork

  • Not a git operation
  • Provided by git forges
  • Creates a copy of the original repo that you can edit

Why fork?

  • Want to contribute to the project
  • Add second remote called upstream which points to original repo

      git remote add upstream <uri>
    • Can bring in latest changes to your fork

PR tips

Review code on the actual git forge site

  • Different look than your editor
  • Removes bias, potentially see stuff differently

Make sure maintainers want these changes

  • Look for issues
  • Consider creating an issue beforehand

Add yourself as a contributor

In the contributors/ directory, add:

  • A file with your forge username.txt
  • A link to your forge profile in the text


Reflog

Location Descripion
HEAD@{0} where HEAD is now
HEAD@{1} where HEAD was 1 move ago
HEAD@{2} where HEAD was 2 moves ago
HEAD@{3} where HEAD was 3 move ago
  • Reflog will show history of all operations, even destructive ones.
  • Can be used to restore after hard reset or long forgotten commits.
  • Records tip of branches and HEAD when updated; tracks every last step of HEAD.

Restoring a deleted but previously committed file using git cat-file:

  • Use git reflog to find the commit hash of the now deleted commit.
  • Use git cat-file -p <hash> to get the tree

      git cat-file -p <commithash>    # gets the tree
      git cat-file -p <tree>          # gets the blob
      git cat-file -p <blob>          # gets the file-hash
      git cat-file -p <file-hash>     # shows contents of file

Restoring a deleted file the fast way

  # Instead of everything above, we could have just done:
  git merge HEAD@{1}


Conflicting Changes

  • It is ok when the same line is modified in two separate commits
  • it Is not ok when the same line is modified in two commits with the same parent

Merge Conflicts

The TOP changes are OURS

  • Should indicating HEAD (which is always local/ours)

      <<<<<<<< HEAD

The BOTTOM changes are THEIRS

  • Should indicate the branch, e.g. main

      >>>>>>>> main

You can manually accept both changes.

To reset to a specific commit:

  git reset --hard <commit hash>

Multi Conflict

The git checkout command can checkout the individual changes during a merge conflict using the –theirs or –ours flags.

  • –ours keeps the version of the file from your current branch (the one you're on before merging)
  • –theirs uses the version of the file from the branch you're merging into your current branch
  git checkout --theirs <path/to/file>
  git checkout --ours <path/to/file>


Rebase Conflicts

Most of the bad rap rebase gets is around conflicts because it rewrites history.

Rarely will we have conflicts on our own stuff

In the "real world," what happens most often is:

  • You switch to a new branch, say fix_bug, which is a copy of main.
  • While you're fixing the bug, someone else merges their changes into main.
  • You fix the bug, and it so happens that you edited the same files (and lines) that the other person did.
  • You open a Pull Request to merge (or rebase) fix_bug into main, then git tells you there's a conflict.
  • You resolve the conflict on your branch.
  • You complete the Pull Request with the conflict resolved.

REMEMBER YOU SHOULD NEVER REBASE MAIN MEANING YOU SHOULD NEVER TYPE THE WORDS:

  git rebase <feature_branch>     # NEVER DO THIS

IF REBASING, YOU SHOULD ONLY TYPE:

  git rebase main                 # rebase onto main
  git rebase master               # rebase onto master

This is saying "rebasing onto main/master."

Detatched HEAD

After rebasing main on the feature branch and conflict arises:

  • Check the branch

    • YOU WILL NOT BE ON ANY BRANCH
    • your branch will be: (no branch, rebasing <featurebranch>)
  • This is called detached HEAD

    • temporary state
    • allows you to resolve the conflict before proceeding with the rebase

Resolving Conflicts

Use git checkout –theirs and git checkout –ours In a merge

  • –ours refers to your current branch
  • this is most likely MAIN/MASTER

    • sinced based bros will be rebasing onto a feature branch
    • and only (fast forward) merging onto main/master

In a rebase

  • –ours refers to the branch you're rebasing onto.
  • in this case we are rebasing from the feature branch onto a different branch
  • it is probably main/master

"Accept current change" is the same as

  git checkout --ours <filepath>
  • <<<<<<
  • top portion of the conflicting file

"Accept incoming change" is the same as

  git checkout --theirs <filepath>
  • >>>>>>
  • bottom portion of the conflicting file

After resolving conflicts

Add the file.

DO NOT COMMIT: YOU ARE STILL ON THE REBASE BRANCH

Instead continue the rebase:

  git rebase --continue

Check the log

  git log --oneline --all

NOTE

  • the commit we discarded is gone from git history
  • still accessible with reflog


Repeat Resolution (rerere)

  • "Reuse recorded resolution"
  • It allows you to ask Git to remember how you've resolved a hunk conflict.
  • Next time it sees the same conflict, Git can resolve it for you automatically.
  • Applies to rebasing and merging
  git config set --local rerere.enabled true
  git config set --local rerere.enabled false # disable rerere
  rm -rf .git/rr-cache            # delete any remaining rerere cache

Conflict After Rebase Example:

  wagslane@MacBook-Pro-2 megacorp % git rebase main
  Auto-merging customers/favs.md
  CONFLICT (add/add): Merge conflict in customers/favs.md
  error: could not apply ad9b194... K
  hint: Resolve all conflicts manually, mark them as resolved with
  hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
  hint: You can instead skip this commit: run "git rebase --skip".
  hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
  Recorded preimage for 'customers/favs.md' # <--- THIS LINE HERE
  Could not apply ad9b194... K

NOTICE:

  Recorded preimage for 'customers/favs.md' # <--- THIS LINE HERE

Rerere is now recording how to resolve the conflict

  • Either accept –ours (main) or –theirs (favs branch)
  • Or accept both by manually editing the file

If we recreate the same conflict from another (third) branch (favs2)

  wagslane@MacBook-Pro-2 megacorp % git rebase main
  Auto-merging customers/favs.md
  CONFLICT (add/add): Merge conflict in customers/favs.md
  error: could not apply ad9b194... K
  hint: Resolve all conflicts manually, mark them as resolved with
  hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
  hint: You can instead skip this commit: run "git rebase --skip".
  hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
  Resolved 'customers/favs.md' using previous resolution. # <--- THIS LINE HERE
  Could not apply ad9b194... K

NOTICE

  Resolved 'customers/favs.md' using previous resolution. # <--- THIS LINE HERE
  • if you open the conflicting file, the changes have already been made to match the first one

Continue the rebase with

  git rebase --continue


Squashing

Squashing groups a series of sequential, small commits into one commit. This is preferred by some teams

  git rebase -i HEAD~n            # n is number of commits we want to squash
  # HEAD is current commit, ~n is n previous commits
  # -i creates an interactive rebase
  1. git will open editor with a list of commits

    • -i flag is interactive
    • Allows us to edit commit history
  2. Change the word "pick" to "squash" for all but the first commit

    • First commit will be at the top of the editor prompt
  3. Save and close the editor

Squashing Best practices

  1. Identify the main branch remote

    • Might have to reset this
    • Most likely origin
  2. Create a temporary branch

    • Rebase is destructive
  3. Perform the rebase on temp branch

    • Emacs git-rebase-mode has its own keybindings; do not need to directly edit, just press "s" on all lines that should be changed from "pick" to "squash"
  4. Delete the main branch

      git branch -d main
  5. Rename temp branch to main

      git branch -m temp_main main
  6. Push and reset the main branch remote

      git push -u origin main
      # git might suggest:
      # git push --set-upstream origin main
      # which does the same thing

Force push

Force push required because this is a destructive operation on the remote main

  git push origin main --force

Your git history is rewritten, but full history is still accessible with reflog.



Stash

The git stash command:

  • Records the current state of your working directory and the index (staging area).
  • Like your computer's copy/paste clipboard.
  • It records those changes in a safe place
  • Reverts the working directory to match the HEAD commit
  git stash
  git stash -m "jdsl v0 almost working" # adds a message
  git stash list

  git stash pop                   # applies the first stash on the stack

  # applies the first stash on the stack but keeps it in the stash list
  git stash apply
  git stash apply stash@{2}       # Apply the third (0, 1, 2) most recent stash

  git stash drop                  # removes a stash without applying
  git stash drop stash@{2}        # Remove the third most recent stash

The pop command will (by default):

  • Apply your most recent stash entry to your working directory
  • Remove it from the stash list.
  • It effectively undoes the git stash command. It gets you back to where you were.

Stash is a Stack Data structure

The git stash command stores your changes in a stack (LIFO) data structure. That means that when you retrieve your changes from the stash:

  • You'll always get the most recent changes first.
  • Stores index and worktree.


Revert

To revert a commit, you need to know the commit hash of the commit you want to revert (git log)

  git revert <commit-hash>

Revert vs Reset

git reset --soft: Undo commits but keep changes staged

git reset --hard: Undo commits and discard changes

git revert: Create a new commit that undoes a previous commit

When to Reset?:

  • Working on your own branch,
  • Just undoing something you've already committed,
  • E.g cleaning everything up so you can open a pull request

When to Revert?:

  • Undo a change that's already on a shared/public branch (especially if it's an older change)
  • It won't rewrite any history, and therefore won't step on your coworkers' toes.


Diff

  # show the changes between the working tree and the last commit
  git diff

  # show the differences between the previous commit and the current state, including the last commit and uncommitted changes
  git diff HEAD~1

  # show the change between two commits
  git diff COMMIT_HASH_1 COMMIT_HASH_2


Cherry-pick

Useful if you want one change from a branch but do not want the entire branch history.

  git cherry-pick <commit-hash>
  • You need a clean working tree (no uncommitted changes).
  • Identify the commit you want to cherry-pick, typically by git loging the branch it's on.
  git cherry-pick <commit-hash>


Bisect

Git bisect is a binary search between known good and known bad commits.

  • Finds the middle commit between a good and bad commit.
  • You check to see if that bug is present in that middle commit.
  • If not present, identify that commit as good. It then searches the middle of that and the bad commit.
  • Repeat until the bug is found.
  • Drastically cuts down time to find bugs.
Commits to Check Max Checks to Find
1 1
2 1
10 4
100 7
1000 10
10000 14
  1. Start the bisect with

      git bisect start
  2. Select a "good" commit with

      git bisect good <commit hash>     # (a commit where you're sure the bug wasn't present)
  3. Select a "bad" commit with

      git bisect bad <commit hash>      # (a commit where you're sure the bug was present)
  4. Git will checkout a commit between the good and bad commits. You must test to see if the bug is present in that commit.

      git show HEAD
  5. Execute git bisect good or git bisect bad to identify the current commit as good or bad.
  6. Loop back to step 4 (until git bisect completes).
  7. Exit the bisect mode with git bisect reset.

      git bisect reset

Then, take action to address the commit:

  # you want to revert the commit that introduced the bug - first "bad commit"
  # you do not want to revert the last good commit
  git revert <bad_commit_hash>

You can refer to commits as HEAD

  • After the first bisect, you are really just saying HEAD is good or bad
  • For each iteration you simply manually check the file for the bug

      git show HASH
    
      git show HEAD                   # most likely
  • git has no way of knowing what is the bug

    • You don't "tell" git what the bug is and/or what to search for.
    • All git is doing is finding the middle commit.

Automating git bisect

From the git bisect man page:

Bisect run If you have a script that can tell if the current source code is good or bad, you can bisect by issuing the command:

$ git bisect run my_script arguments

Note that the script (my_script in the above example) should exit with code 0 if the current source code is good/old, and exit with a code between 1 and 127 (inclusive), except 125, if the current source code is bad/new.

This could be accomplished by a script that:

  • searches a file for whatever the problem is
  • returns 0 if the source is good
  • returns 1+ if the source is bad

For example:

  #!/bin/sh
  # bisect-script.sh
  if grep -q "SCANNING" "scripts/scan.sh"; then
      exit 1
  else
      exit 0
  fi
  • save and mark this script as executable somewhere in the repo
  • mark your good and bad initial commits

Then run:

  git besect run ./bisect-script.sh


Worktree

A worktree (or "working tree" or "working directory"):

  • The directory on the local filesystem where code tracked in Git lives.
  • Usually just the root of your Git repo (where the .git directory is).
  • Contains:

    • Tracked files - files that Git knows about
    • Untracked files - files that Git doesn't know about
    • Modified files - files that Git knows about that have been changed since the last commit
  git worktree list

Stash is still useful for small and short-lived changes. Worktrees are better for long-lived changes.

Worktrees accomplish a similar goal as stash, branch, clone, etc:

  • Allow you to work on different changes without losing work.

Linked worktrees but are particularly useful when you want to:

  • Switch back and forth between the two change sets without having to run a bunch of git commands (not branches or stash).
  • Keep a light footprint on your machine that's still connected to the main repo (not a clone).

The Main Worktree

  • Contains the .git directory with the entire state of the repo
  • Heavy (lots of data)
  • A new main working tree requires a git clone or git init.

Linked Worktree

  • Contains a .git file with a path to the main working tree
  • Light (essentially no data), about as light as a branch.
  • Can be complicated to work with when it comes to env files and secrets.
  git worktree add <path> <branch> # create a new linked worktree

No Duplicate Branches

Linked worktrees behave just like a "normal" git repo. You can:

  • create new branches
  • switch branches
  • delete branches
  • create tags
  • etc

BUT there is one thing you cannot do:

  • you cannot work on a branch that is currently checked out by any other working tree (main or linked worktree).

Example: try to switch to main/master from a linked worktree.

  • tldr; you can't

Upstream

Linked Worktrees are tracked in the .git/worktrees directory

When you make a commit in a linked worktree, that commit is automatically reflected in the main worktree Why this makes sense:

  • The linked worktree doesn't have a .git directory
  • It is not a separate repository.
  • It is just a different view of the same repository.

You can almost think of a linked worktree as just another branch in the same repo but with its own space on the filesystem.

Delete Worktrees

  git worktree remove <worktree name>

NOTE

  • This deletes the worktree: it removes the worktree directory from the filesystem
  • It does not remove the branch.
  • Run git branch in the main git repo and you will see the branch without the "+" prefix which a worktree will have.

Alternatively:

  • Delete the worktree directory manually
  • Then prune all worktrees

      git worktree prune


Tags

A tag is a name linked to a commit that does not move between commits, unlike a branch.

Tags can be created and deleted, but not modified.

  git tag                         # lists all tags
  git tag -a "tag name" -m "tag message" # create a tag on the current commit

Semver - Semantic Versioning

Naming convention for software - vMajor.Minor.Patch

  • Major: breaking changes
  • Minor: safe features
  • Patch: safe bug fixes

It has two primary purposes:

  1. To give us a standard convention for versioning software
  2. To help us understand the impact of a version change: if it's safe (how hard it will be) to upgrade to

Conventional Tags

Tags are used for all sorts of reasons. Sometimes they're used to denote releases. Tags that follow semver are common.

  git tag -a v3.10.2 -m "Fixed a lil bug"

#git