Production-ready guide covering git advanced commands quick reference guide with implementation patterns, code examples, and anti-patterns for enterprise engineering teams.
This reference collects the git commands and configuration patterns that surface when everyday workflows hit their limits. It matters when git push is rejected after a rewrite, when a commit must be amended after it’s been shared, when history must be searched across every branch, or when a repository state must be rescued after a mistaken reset. Every entry below is a real, ship‑built git subcommand, flag, or config key – nothing invented, nothing attributed to a commercial product. Use it as a lookup table for the moments where the porcelain layer stops helping and the plumbing layer begins.
git log --graph --oneline --all --decorate displays the full branch graph, every head, and short subject lines. Pair --grep=<pattern> with --all-match or --invert-grep to filter by commit message content. For line‑level searches, git log -S<term> finds commits that added or removed the given text string, regardless of when it was introduced.
git blame -L 42,42 path/to/file.py annotates line 42 with the last commit and author that modified it. The -L range accepts start,end, start,+lines, or +start,+lines. Note that git blame respects the git blame.ignoreRevsFile config if a .git-blame-ignore-revs file exists; it does not skip lines that are part of a merge commit’s first parent by default.
git reflog show --all lists every ref update across all repositories on the machine, including dropped commits, branch creations, and checkouts. Each entry has an @{n} syntax: git checkout @{1} returns you to the previous HEAD, irrespective of branch. The reflog expires after the period set by gc.reflogExpire (default 30 days) and gc.reflogExpireUnreachable (default 90 days for unreachable objects). After expiration, git fsck --unreachable can still locate dangling objects, but they are no longer reachable via git reflog.
git commit --amend --no-edit replaces the tip commit with the current staged changes, preserving the original commit message. If no changes are staged, the command opens the editor (or uses commit.gpgsign/ commit.template settings) without altering the message. To change the message without touching the snapshot, use git commit --amend alone, which invokes the editor.
git rebase -i HEAD~5 opens the sequence editor with the last five commits. Each line follows the format action <commit>, where actions are pick, reword, edit, squash, or fixup. squash merges the commit message as well as the changes; fixup discards the message and keeps only the changes. Sharp edge: reordering or dropping commits in an interactive rebase rewrites every subsequent commit hash. If any of the rewritten commits have been pushed to a shared remote, the history diverges and git push will be rejected unless --force-with-lease is used.
git reset --soft HEAD~1 moves the current branch pointer backward one commit while keeping all changes from that commit staged. git reset --mixed HEAD~1 (the default) unstages the changes but leaves them in the working directory. git reset --hard HEAD~1 discards both the commit and all its changes—this action is irreversible without git reflog or object recovery. A common confusion: --soft leaves changes staged, --mixed leaves them unstaged but present, and --hard wipes them entirely. Always verify with git status before and after.
git filter‑branch and the modern alternativegit filter-branch --tree-filter 'rm -f path/to/file' HEAD~1..HEAD removes a file from every commit in the range. This command is deprecated in Git 2.33+ because of performance issues and edge cases with environment variables. The recommended replacement is git filter-repo, a standalone Python script installed via pip install git-filter-repo. It offers --blob-callback, --msg-callback, and --path-rename flags with comparable functionality but a different CLI surface. If you must stay within built‑in git, be aware that filter‑branch rewrites every commit’s SHA, updates every branch tip, and can produce duplicate commits if the same tree appears under multiple parents.
git fsck to find dangling objectsgit fsck --unreachable --dangling --lost scans the object database for commits, trees, and blobs not reachable from any ref. Output lines begin with unreachable blob <hash>, unreachable tree <hash>, or unreachable commit <hash>. To recover a specific commit, git checkout <hash> creates a detached HEAD at that commit, from which you can git cherry-pick or git merge the work into a current branch.
If the reflog entry you need has expired, you can still locate the object by its hash if you have it. Otherwise, git fsck --unreachable will list it. Once identified, git checkout -b <new-branch> <hash> creates a recoverable branch at that point. Production tip: many teams enable gc.pruneExpire to a longer window (e.g., 180 days) in environments where history‑rewriting operations are frequent, reducing the window in which reflog‑based recovery is possible.
When a teammate runs git push --force on a branch you’ve both been working on, your local reflog still contains the old tip. git fetch origin followed by git log origin/<branch>..HEAD shows commits you have locally that the remote no longer does. git rebase origin/<branch> or git merge origin/<branch> can then re‑integrate those commits into your current work. If the remote branch was force‑pushed with a new history, git reflog on your local copy will show the old ref as refs/remotes/origin/<branch>@{1}; you can reset your local branch to that point with git reset --hard refs/remotes/origin/<branch>@{1}.
git worktree add -b feature/login ../worktrees/feature-login main creates a new worktree tied to the same repository, checked out at main with a new feature/login branch. Each worktree has its own .git file pointing at the shared GIT_DIR. Sharp edge: worktrees share the same index. Running git checkout in one worktree can affect the index visible in another, leading to unexpected staging states. Always git status in the specific worktree you’re operating in.
git fetch --prune updates all remote‑tracking refs and removes any that no longer exist on the remote. Without --prune, stale refs accumulate and git branch -r shows branches that have been deleted on the server. To also prune unreachable local objects after fetching, pair with git remote prune origin or configure remote.<name>.prune in .gitconfig.
git remote set-head origin -a makes the local master (or main) track origin/master. Without this, git pull may complain about ambiguous refnames. An alternative, per‑branch approach: git branch --set-upstream-to=origin/main main establishes the tracking connection for a single branch.
git cherry‑pick across divergent historiesgit cherry-pick <commit-hash> applies the change introduced by the named commit onto the current branch, creating a new commit with a different SHA. If the commit’s changes conflict with the current state, git pauses and marks conflicts in the usual <<<<<<< / ======= / >>>>>>> format. Resolve the conflicts, git add the resolved files, then git cherry-pick --continue to proceed. git cherry-pick --abort cancels the operation and resets HEAD to the state before the cherry‑pick began.
.gitconfig keys used in advanced workflowspull.rebase = true – makes git pull rebase the local branch onto the remote instead of merging. Set globally with git config --global pull.rebase true.merge.conflictstyle = diff3 – uses the three‑way conflict format (<<<<<<<, |||||||, >>>>>>>) instead of the default two‑way. Helpful when you need to see the original line that existed before both branches diverged.diff.tool = meld – if meld (or any diff‑capable tool installed on the system) is present, git difftool opens it. The tool must accept the standard git difftool arguments: git difftool --dir-diff for directory‑level comparison.commit.gpgsign = true – signs every git commit with the GPG key associated with the author identity. Pair with git config --global user.signingkey <key-id> to avoid prompts.gpg.program = /usr/local/bin/gpg2 – explicitly selects the GPG binary if the default is not found.GIT_EDITOR – overrides the editor used by git commit, git rebase -i, and other commands that invoke an editor. If unset, git falls back to core.editor config, then to $EDITOR, then to vi.GIT_SEQUENCE_EDITOR – specifically overrides the editor for git rebase -i sequences. Useful for scripting rebases: GIT_SEQUENCE_EDITOR='sed -i \"s/^pick/rewrite/\"' git rebase -i HEAD~5.HOME – git’s default config directory is $HOME/.gitconfig. On systems where multiple git installations coexist (e.g., via Homebrew and a system package), GIT_CONFIG_GLOBAL can point to an alternate global config file.GIT_PAGER – overrides $PAGER for all git output. Setting GIT_PAGER=cat disables paging, which is handy for scripting: git log --oneline | head will always show the full list instead of stopping at the terminal height.git push --force vs --force-with-leasegit push --force unconditionally overwrites the remote ref, discarding any commits the remote has that your local history does not know about. git push --force-with-lease (available since Git 1.8.2) only updates the remote if the remote ref is still at the hash you expect (i.e., it “leases” the ref and refuses to overwrite if someone else has pushed in the meantime). Production rule: always prefer --force-with-lease. The only case where --force is safe is when you own the branch exclusively and have confirmed no one else has fetched or pushed to it.
Checking out a commit hash, tag, or remote‑tracking ref directly (git checkout v1.2.3 or git checkout <hash>) puts you in a detached HEAD state. Any commits you make here are not on any branch and will be lost if you switch branches or checkout another ref without git branch -c first. To make work permanent, git checkout -b <new-branch> immediately after the detached checkout, or git stash the changes and then create the branch.
git gc and git repack side‑effectsgit gc (garbage collection) runs automatically on push or fetch if the repository has accumulated many loose objects. It packs objects, removes unreachable ones, and can temporarily increase disk I/O. git gc --prune=now forces immediate pruning of objects older than the configured expiry. git repack -a -d aggressively packs all objects into a single pack, deleting loose objects. Caution: on very large repositories, git repack can take hours and may not respect the transfer.fetchPrune or gc.pruneExpire settings, potentially removing objects that reflog‑based recovery depends on. Always verify with git fsck after aggressive repacking.
git submodule update gotchasgit submodule update --init --recursive clones each submodule’s recorded commit. If a submodule’s remote URL has changed or the recorded commit no longer exists on the remote, the command fails with fatal: repository '...' does not exist. The error message does not indicate whether the issue is a URL mismatch or a missing commit. To diagnose, git submodule status shows + (initialized), - (not initialized), or * (new). git submodule sync updates the .gitmodules file’s URLs from the config, and git submodule update --init then fetches the correct commit.
git blame performance on histories with many mergesgit blame on a file with a long history and numerous merge commits can be slow, because it must walk the full ancestry graph to determine the “last modifying” commit. The --first-parent flag limits the walk to the first parent of each merge, which speeds up the operation but may miss changes that were introduced on a merged side‑branch. If you need blame accuracy across merge branches, be prepared for longer latency; for quick inspection, git log -p --follow <file> often provides a faster, if less precise, view.
git worktree race conditions with concurrent checkoutsWhen two worktrees share the same repository and one runs git checkout <branch> while the other is in the middle of git add/git commit, the index can become inconsistent. The worktree performing the checkout may report “paths changed on disk” or refuse the checkout until the index is refreshed with git reset. Mitigate by ensuring only one worktree modifies the index at a time, or by using git stash before switching branches across worktrees.
git rebase onto vs. interactive rebasegit rebase --onto <newbase> <upstream> <branch> re‑bases the range <upstream>..<branch> onto <newbase> in a single step, without opening an editor. This is useful for linearizing feature branches: git rebase --onto main feature-branch main reapplies every commit on feature-branch that is not already reachable from main. Sharp edge: if <upstream> is an ancestor of <branch>, the command may silently do nothing or produce an empty rebased history. Always verify the result with git log --oneline after the operation.
git stash with untracked filesgit stash by default only stashes tracked files. git stash --include-untracked (or the short form git stash -u) includes untracked files in the stash. git stash --all also stashes ignored files (respecting .gitignore). Production tip: if your workflow depends on untracked assets (e.g., generated documentation, build artifacts), always use git stash -u to avoid losing them when you later git stash pop on a different branch.
*This guide is intentionally terse and command‑focused. It does not replace the git man pages or the official documentation, but it surfaces the exact flags, error messages, and configuration patterns you’ll encounter when the standard workflow breaks down.*
This page was rewritten on 14 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.
We use cookies for analytics (Google Analytics) and advertising (Google AdSense) to improve your experience and support free content. Privacy Policy