A project directory can key a tmux session directly by its path, created or attached by typing two letters. The whole thing, from my dotfiles:
alias th='td .'
alias ty='td ~'
alias tg='td "$(git rev-parse --show-cdup 2>/dev/null)"'
td () (
set -e; cd "${1:-.}"; shift
exec tmux new -ADs "$PWD;" set @session_name "${PWD##*/};" "$@"
)The trick is the trailing ; inside an argument. tmux treats an unescaped trailing semicolon as a command separator, so that single exec is a two-command tmux batch with an optional caller-supplied tail. First, new -ADs "$PWD"—attach the session if it exists, create it otherwise (-A), detaching other clients (-D), named by the absolute path. Second, set @session_name "${PWD##*/}"—record the basename as a session option. If the caller supplies "$@", those arguments append further tmux commands; the ordinary th, ty, and tg wrappers supply none.
For paths tmux accepts and resolves unchanged, naming the session by $PWD makes th idempotent: run it twice in the same directory and you land in the same session; run it somewhere else and you get a new one. The full path is unambiguous inside that naming domain but ugly in a status line, hence the second command. tmux allows arbitrary user options prefixed with @, and the status line renders whichever name is available:
set -g status-left "... #{?@session_name,#{@session_name},#{session_name}} ..."A format conditional: show the pretty basename when the option exists, fall back to the real full-path session name otherwise. Unique identity and readable display, with no registry mapping one to the other.
The wrappers always supply an argument. Calling td directly with no argument exits at shift under set -e, before reaching tmux.
The wrappers pick the directory: th is here, ty is home, tg is the git root—rev-parse --show-cdup prints the relative path up to the toplevel, and its empty output at the root falls through ${1:-.} to .. The function body is a subshell, ( ) instead of { }, so set -e and the cd never touch the interactive shell—though exec replaces the subshell with tmux anyway, which is the point: no wrapper process lingers.
Raw paths are not a general tmux name encoding. tmux 2.8 rejects : and . in session names; later versions can normalize them. A directory ending in a backslash would escape the separator this function appends. A semicolon within the path does not split it: the argument parser only removes the final separator. I accepted those constraints because I don’t name project directories that way. A general one-directory/one-session contract would encode the path first.