A repo is as database containing a tree of commits. Then you got the concept of branch, which points to a specific commit. Then there's the special pointer HEAD which is the latest commit for the current worktree.
When you checkout (now switch) a branch, HEAD is now the same as the branch (they point to the same commit). When you do operation like commit, reset, reset,... both the head and the branch are updated. So if a remote node tries to update the local node via a push on its end, that would mess up the local worktree. So you create a bare repo instead which can't contain a local worktree.
Note: A worktree is computed from the initial commit to the commit currently identified by HEAD by following the parent information on each commit.
The staging area is like a stored snapshot of what you would like to commit. You can always create patch between HEAD and the worktree, edit it, and then save the patch as the commit, then apply the leftover to the worktree. The staging just make that easier. It's a WIP patch for the next commit.