the problem worktrees solve

A branch switch mid-task (to fix an urgent bug on main) usually means stashing whatever you were doing, switching, fixing, switching back, and unstashing. A worktree is a second working directory, backed by the same .git history, with a different branch checked out — no stashing required.
git worktree add ../hotfix main | https://git-scm.com/docs/git-worktree | creates ../hotfix as a new directory with "main" checked out, sharing this repo's history |'wt_add1'
git worktree list | | shows every worktree linked to this repo and which branch each has checked out |'wt_add2'
git worktree remove ../hotfix | | removes a worktree once you're done with it |'wt_add3'
The same branch can't be checked out in two worktrees at once — git blocks that, since both would be trying to move the same branch pointer.

the problem submodules solve

A submodule embeds one git repository inside another, at a specific pinned commit — for pulling in a dependency you need to build from source, or sharing code between projects without copy-pasting it.
git submodule add <url> <path> | https://git-scm.com/docs/git-submodule | adds another repo at <path>, recorded in a tracked .gitmodules file |'wt_sub1'
git clone --recurse-submodules <url> | | clones a repo AND fetches every submodule's pinned commit in one step |'wt_sub2'
git submodule update --init --recursive | | the fix if you cloned without --recurse-submodules: submodule folders exist but are empty |'wt_sub3'
The parent repo doesn't track the submodule's files — it tracks one thing: which exact commit hash the submodule should be at. Going into the submodule's directory and running normal git commands there checks it out in a detached HEAD state (the same detached HEAD explained in Git Internals), which trips up almost everyone the first time.

subtrees: the other way to embed a repo

A subtree copies another repo's files directly into your history (no pointer, no separate .gitmodules, no detached HEAD to manage) — anyone who clones your repo gets everything immediately, with zero extra steps.
git subtree add --prefix=vendor/lib <url> main --squash | https://git-scm.com/docs/git-subtree | pulls another repo's "main" branch in as a squashed set of commits under vendor/lib |'wt_subtree1'
git subtree pull --prefix=vendor/lib <url> main --squash | | pulls upstream updates into that same folder later |'wt_subtree2'

which one, when

SituationUse
Fix a bug on another branch without losing your current work-in-progressworktree
Build docs and code from the same repo side by side, in CIworktree
Depend on another team's library, pinned to an exact version, updated deliberatelysubmodule
Vendor a small library's source directly, no extra clone step for contributorssubtree

related topics

Git Internals — what detached HEAD actually means, which every submodule checkout hits.
Git Cheat Sheet — the everyday commands both worktrees and submodules build on.

reference

git-scm.com — git worktree
Pro Git — Submodules
git-scm.com — git subtree