The git checkout command allows you to switch branches by updating the files in your working tree to match the version stored in the branch that you wish to switch to.
You can think of it as a way of switching between different workspaces.
HEAD is used to represent the current snapshot of a branch. For a new repository, Git will by default point HEAD to the master branch. Changing where HEAD is pointing will update your current active branch.
The ~(tilde) and ^(caret) symbols are used to point to a position relative to a specific commit. The symbols are used together with a commit reference, typically HEAD or a commit hash.
~<n> refers to the <n>th grandparent. HEAD~1 refers to the commit's first parent. HEAD~2 refers to the first parent of the commit's first parent.
^<n> refers to the the <n>th parent. HEAD^1 refers to the commit's first parent. HEAD^2 refers to the commit's second parent. A commit can have two parents in a merge commit.
Whenever you switch to another branch with uncommitted changes (or new files added) in your working tree, these uncommitted changes will also be carried to the new branch that you switch to. Changes that you commit will be committed to the newly switched branch.
However, if Git finds a conflict between the files from the newly switched branch and the uncommitted changes from the previous branch, you will not be allowed to switch to the other branch. You must commit or stash those changes first before switching branches.
You can think of stash as a drawer to store uncommitted changes temporarily. Stashing allows you to put aside the “dirty” changes in your working tree and continue working on other things in a different branch on a clean slate.
Uncommitted changes that are stored in the stash can be taken out and applied to the original branch and other branches as well.