Tags and releases
A tag is a named reference to a specific commit. While branches move forward as you add commits, tags stay fixed. They are the standard way to mark release points in your project's history.
git tag — Mark important commits
Git supports two types of tags. A lightweight tag is simply a pointer to a commit — think of it as a branch that never moves. An annotated tag (git tag -a) is a full Git object with the tagger's name, email, date and a message. Annotated tags are recommended for releases because they carry metadata and can be cryptographically signed.
Tags are not pushed by default — you need git push origin --tags to share them with the remote. To delete a remote tag, use git push origin :refs/tags/v1.0.0.
Semantic versioning
Most open-source projects follow Semantic Versioning (SemVer) for their tags: vMAJOR.MINOR.PATCH. Increment MAJOR for breaking changes, MINOR for new backwards-compatible features, and PATCH for bug fixes. For example, going from v1.2.3 to v1.3.0 signals a new feature with no breaking changes.
Quick reference: v1.0.0 = first stable release. v0.x.y = anything may change. Prefix the tag with v by convention (e.g., v2.1.0).
Git configuration
Git's behavior is controlled by configuration files at three levels. Understanding how they cascade is the key to a comfortable, personalized workflow.
git config — Local, global and system settings
Git reads configuration from three scopes, each overriding the previous: system (/etc/gitconfig, applies to every user), global (~/.gitconfig, your user-wide settings), and local (.git/config, per-repository). Use --global for your identity and preferences, and no flag (or --local) when a single project needs different settings.
Useful aliases
Aliases let you create shortcuts for commands you type often. They are stored in your ~/.gitconfig file and can save significant typing over the course of a day. Here are some popular ones to get you started.
Pro tip: You can also define aliases that run shell commands by prefixing them with !. For example: git config --global alias.undo '!git reset --soft HEAD~1'.
Sharing patches
Before GitHub pull requests existed, developers shared code changes via patch files sent by email. The patch workflow is still widely used in projects like the Linux kernel, and it remains a useful skill when you need to share changes outside of a hosted platform.
git format-patch — Create patch files
git format-patch turns commits into .patch files that contain the diff along with the commit message, author and date. Each commit becomes its own numbered file, ready to be emailed or shared in a ticket.
git apply — Apply patch files
git apply takes a patch file and applies the changes to your working tree without creating a commit. If you want to preserve the original commit metadata (author, message), use git am instead, which applies the patch and creates a commit in one step.
Work with multiple copies
Sometimes you need to work on two branches at the same time — for example, applying a hotfix while a feature is in progress. Instead of stashing and switching, Git lets you maintain multiple working directories from a single repository.
git worktree — Multiple working directories
git worktree add creates a new working directory linked to the same repository. Each worktree checks out a different branch, and they all share the same .git data. This means no extra clone, no extra disk space for the history, and no risk of losing uncommitted work when you switch context.
Package your code
Need to send a snapshot of your project to someone who doesn't use Git, or produce a release artifact for deployment? git archive has you covered.
git archive — Export a clean archive
git archive exports the contents of a tree (a commit, branch or tag) as a .zip or .tar.gz file. Unlike simply zipping the folder, git archive excludes the .git directory and respects your .gitattributes export-ignore rules. It's the cleanest way to produce a distributable snapshot.
Git hooks
Hooks are scripts that Git executes automatically at key points in your workflow. They let you enforce coding standards, run tests, validate commit messages, or prevent pushes to protected branches — all without relying on human discipline.
The most commonly used hooks are:
pre-commit— runs before a commit is created. Use it to lint code, run formatters, or check for secrets.commit-msg— runs after you write the message. Use it to enforce a format like Conventional Commits.pre-push— runs before pushing. Use it to run the test suite and block pushes if tests fail.
Team sharing: Hooks in .git/hooks/ are not versioned. To share them, place your scripts in a tracked folder (e.g., .githooks/) and run git config core.hooksPath .githooks. Tools like Husky or pre-commit automate this for you.
Git submodules
A submodule embeds one Git repository inside another. The parent repo records which commit of the submodule to check out, so you can pin exact versions of shared libraries or components. It's the Git-native answer to "repos inside repos."
Submodules are powerful but come with a learning curve. When cloning a project that contains submodules, remember to initialize and update them. When updating a submodule, you commit the new reference in the parent repo.
Alternatives: If submodules feel too complex, consider git subtree (merges the external code directly into your tree) or a language-specific package manager for third-party dependencies.
With tags, hooks, worktrees, patches, archives and submodules in your toolkit, you're equipped for virtually any Git workflow. The best way to internalize these commands is to practice them in a real project — start small and build up.