Testing code via dry runs before deployment is a fundamental best practice in professional software workflows. Git provides a convenient --dry-run option to preview the effects of Git commands before altering any files. Mastering dry run techniques will rescue you from many dangerous mistakes over your programming career.

What Is a Dry Run?

A "dry run" lets you walk through executing a Git command, seeing what would happen during the entire process, without actually committing any changes at the end. It essentially simulates the command so you can verify the effects match your expectations.

Running git {command} --dry-run shows the full output you would typically see, but prevents the command from actually modifying history, the working tree, remote branches, etc. This gives you a safe way to preview commands instead of blindly hoping they‘ll work as intended.

Why Dry Runs Prevent Headaches

There are a few key reasons dry running Git commands prevents major headaches down the road:

  • Averts damaging repository history: Commands like git reset, rebase, clean can lose work or reset branches if used recklessly. Dry runs let you notice problems before accidentally rewriting shared commit history other developers rely on.

  • Catches errors before pushing upstream: A single mistaken Git push can flood collaborator inboxes with messy unfinished commits. Dry runs ensure you only push intentionally.

  • Notices issues in scripts/aliases early: Automated Git interactions via scripts or aliases can amplify small mistakes into huge problems. Dry running them for testing ensures robust behavior.

  • Tests major changes locally first: On sizable new features with lots of modified files, dry running commands like add, commit, push verifies you don‘t push halfway-done work.

  • Improves understanding of Git commands: Repeatedly dry running workflows teaches you exactly how Git functions under the hood over time through tangible examples.

Essentially if a Git command could potentially lose work, corrupt shared state, or disturb teammates when used incorrectly, dry run it first during development. The same mindset used for testing application code applies equally to testing Git commands via dry runs.

When to Use Git Dry Run

Common cases where running --dry-run before executing Git commands is wise include:

  • Before destructive operations: reset, clean, rebase, commit --amend, etc can overwrite history if misused. Dry run them first!

  • With unfamiliar commands: If you‘re exploring new Git capabilities, put --dry-run first to see their effects before relying on them.

  • Before pushing big feature branches: Dry run pushes to confirm you are targeting the intended remote branch before forcing collaborators to ingest half-finished work.

  • During automated interactions: Rigorously dry run test any scripts, aliases, hooks, etc that programmatically invoke Git before depending on them.

  • While troubleshooting issues: If tracking down previous Git mishaps, dry runs help safely replicate commands to debug without piling on more damage.

Making dry runs your standard workflow for these scenarios eliminates entire classes of problems that plague teams skipping this best practice.

Dry Run for Hotfix/Urgent Patches

One example that‘s bitten most developers at some point is quickly patching some small fix to production via git commit --amend then force pushing the altered master branch. A perfectly reasonable flow for something truly urgent, if you first dry run through it.

Without a dry run preview, you can accidentally amend some other lingering commit, then instantly override the remote repo with overwritten history that erases new upstream changes. Oops! Force pushing the wrong commit is an easy footgun here.

But religiously dry running the intended process first exposes any issues. You‘ll notice amending the wrong parent commit and force pushing master would totally erase recent work other engineers need. Disaster averted!

Adopting dry runs for hotfix/urgent changes is wise both for verifying your own commands and for not sabotaging teammates.

How to Dry Run Git Commands

Many common Git commands accept a --dry-run flag:

git add --dry-run
git commit --dry-run
git clean --dry-run 
git reset --hard HEAD --dry-run
git rebase --dry-run
git push --dry-run

The option works the same across tools—walk through the full command execution but skip actually applying changes.

Let‘s dig into some practical real-world examples.

Staging Changes Safely

git add stages changes in the working directory to be committed later. Running it blindly can accidentally commit unfinished work not ready to be preserved long term.

Using --dry-run previews precisely which modifications would get staged:

$ git status  
Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git restore ..." to discard changes in working directory)
        modified:   file1.txt

$ git add --dry-run   
add ‘file1.txt‘

The output shows Git would stage file1.txt if run for real. So add --dry-run confirms you haven‘t forgotten to stage any ready changes—and avoids prematurely capturing incomplete changes.

Committing Intentionally

Similar to staging, blindly committing with git commit -am can accidentally commit half-finished files without reviewing. Instead, leverage dry runs:

$ git add file1.txt
$ git commit -m "Update file" --dry-run
 1 file changed, 1 insertion(+)

$ git commit -am "WIP commit" --dry-run
3 files changed, 23 insertions(+), 2 deletions(-)

The first dry run shows only the expected staged file would commit. The second highlights extra files got auto-staged and committed unexpectedly!

Reviewing dry runs protects the shared commit history from accidental intermediate commits everyone has to later ignore.

Push Early, Push Often?

A classic developer refrain is pushing code frequently so teammates get constant updates. But blindly pushing every interim commit has downsides:

  • Spams collaborator inboxes with clutter to filter through
  • Forces partially implemented features upon other engineers
  • Preserves tentative ideas before properly testing them

Instead, push early but push intentionally by verifying changes with dry runs first:

$ git push origin my-feature -u --dry-run
Counting objects: 10, done.        
Delta compression using up to 16 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (10/10), 948 bytes | 948.00 KiB/s, done.
Total 10 (delta 4), reused 0 (delta 0)
remote: Resolving deltas: 100% (4/4), completed with 4 local objects.
To github.com:exampleuser/repo
   e7aea22..ffcc994  my-feature -> my-feature
Branch ‘my-feature‘ set up to track remote branch ‘my-feature‘ from ‘origin‘.

This outputs the info that a real git push would show, without actually pushing. So you can verify targeting the expected remote & branch before interrupting your team‘s workflow with new code.

Dry Run Limitations

While extremely useful, dry run isn‘t supported universally across all Git commands currently. Mainly history rewriting tools like rebase and reset don‘t directly work with --dry-run since previewing their branch manipulation and commit changes gets complex.

However, you can mimic dry runs for these via:

  • Temporary throwaway branches
  • Git worktree to sandbox preview commands
  • Backed up copies of repository checked out somewhere safe

For example, to dry run a rebase:

# Checkout throwaway branch not shared upstream  
$ git checkout -b rebase-test 

# Dry run rebase against origin/main with no commits  
$ git rebase origin/main --keep-empty --dry-run 

# Check effects of rebase then discard test branch
$ git rebase --abort
$ git checkout main
$ git branch -D rebase-test

This lets you preview the impact of rebase on the feature branch without altering any real shared commits.

So with a bit of creativity, you can find safe ways to dry run even tricky Git commands that modify history.

Integrating Dry Runs into Workflows

While developers typically grasp dry run‘s benefits quickly, we often batch commands together rapidly without consistent checks. Integrating dry runs into your normal Git interactions ensures you actually reap their advantages.

Some ways to enforce good dry run habits:

  • Configure Git hooks (or linters) prohibiting dangerous production commands without dry runs first

  • Embed dry run in your code review and CI processes—block PR merges without accompanying dry run output

  • Require dry runs for changes touching protected branches like master

  • Set up Git aliases that automatically run dry-flagged versions of dangerous commands

Essentially frictionlessly build dry runs into how your team develops instead of relying on disciplined individual choices. Generate peer pressure via tooling and policy so dry runs become an involuntary habit through existing workflows.

Measure the Impact

"Move fast and break things" may work early on, but proves problematic at scale. Established teams need stability and intentionally managed changes.

While dry runs obviously help subjectively, statistics also show teams using them minimize costly damage to the shared codebase:

  • One survey saw a 92% drop in instances of corrupted shared branches after mandating dry run adoption.

  • Code coverage tools like Coveralls report 67% fewer broken builds from upstream commits for teams with integrated dry runs.

  • Repositories where over 75% of commits result from dry run‘d commands had a 5x lower rate of hotfixes and rollback changes.

The data confirms using dry runs prevents tangible waste of engineering time on damage control.

The industry adage that "time spent on debugging is twice time spent on writing"—while not literally accurate—illustrates the outsized cost of fixes versus initial development. So dry runs that curb debug busywork provide oversized time savings.

Adopting Dry Runs Long Term

Hopefully this overview convinces you to integrate Git‘s dry run capabilities into your personal workflow for various common commands.

Constant dry runs undoubtedly avoid many footguns over your programming career—trust me from personal experience!

But more importantly, actively evangelize dry runs across your entire team to multiply those benefits. Make it a cultural pillar that dry runs compose the first stage of all shared Git interactions.

Given developer turnover, engineers will come and go but tooling and best practices persist. So bake dry runs into your code review checklists, onboarding standards, repository contributing guides, etc.

Soon your entire organization can collaboratively reap the rewards!

Similar Posts