kendrick/symfony-debug-toolbar-git
Adds Git context to the Symfony debug toolbar, showing the current branch, commit hash, and working tree status right in your development toolbar so you can confirm which code version you're testing without leaving the browser.
This is a dev-only Symfony bundle that pushes the current git log -1 output (hash, author, date, subject) into a Web Profiler panel and toolbar item. It is not a Laravel package — there is no service provider, no config publish, no Blade directive. Treat it as a reference implementation if you are porting the idea to Laravel, and as a drop-in only inside a Symfony app.
Minimal steps in a Symfony project:
composer require --dev kendrick/symfony-debug-toolbar-git
Register the bundle in the dev environment only.
// Symfony 3.x: app/AppKernel.php
if (in_array($this->getEnvironment(), ['dev', 'test'])) {
$bundles[] = new Kendrick\SymfonyDebugToolbarGitBundle\KendrickSymfonyDebugToolbarGitBundle();
}
// Symfony 4/5: config/bundles.php
return [
// ...
Kendrick\SymfonyDebugToolbarGitBundle\KendrickSymfonyDebugToolbarGitBundle::class => ['dev' => true],
];
Then bin/console cache:clear, load any page with the web debug toolbar, and look for the new Git entry. If nothing appears, open the profiler (/_profiler) and check the "Git" panel exists — the toolbar can hide items when the response is a redirect or a non-HTML format.
Where to look first:
DependencyInjection/Configuration.php — this is where any options (repo path, binary path, enabled flag) actually live; the README on a 2018 package is usually thinner than the code.DataCollector class — that is the whole package. A short exec('git log -1 ...') plus dump(), and now a reset() method (added in 1.4.5). Start reading at collect() and reset().Resources/views/ for the toolbar template and icon if you want to restyle it.First use case: you have three checkouts of the same app (local, staging, a preview container) and you keep debugging against the wrong one. The toolbar commit hash answers "what am I actually looking at" in one glance, without shelling into the box.
Deploy verification loop. The highest-value pattern is pairing the toolbar's commit hash with your deploy marker. Open the app after a deploy, confirm the short SHA matches the pipeline artifact, then start testing. This catches the classic "deploy succeeded but opcache/cache kept the old code" failure in seconds.
Multi-repo and monorepo setups. By default the collector runs git relative to the project root (the kernel's getProjectDir()/getRootDir()). In a monorepo where the Symfony app is a subdirectory, or where the deployed app is a build artifact with no .git, point the collector at an explicit repo path if the bundle exposes one; otherwise override the data collector service and inject the path yourself. For split repos (app + separate content package), pick the repo whose commit actually determines behaviour — usually the app.
Docker/CI builds. Never copy .git into production images just for this. Two clean options: (a) accept that the panel is dev-only and let it fail silently in containers without git, or (b) bake the SHA into the image as an env var (GIT_COMMIT) at build time and replace the data collector with a trivial service reading that env var. Option (b) also gives you the value in prod error pages and health endpoints, which the bundle cannot do because it is dev-only.
Long-running runtimes. Since 1.4.5 the collector implements reset(), which the profiler calls between requests. That is what makes the panel safe under RoadRunner, Swoole, and FrankenPHP worker mode, where the collector service is reused across requests instead of being rebuilt from the container. If you fork or subclass the collector, keep a reset() that clears whatever you cached in collect() — otherwise you can leak one request's (or one deployment's) commit data into the next.
Extending the panel. The collector is a normal Symfony DataCollector. If you want extra fields (branch name, tag, dirty working tree, CI build number), decorate or subclass it, add to the $data array in collect(), and add rows to the toolbar template. This is the cheapest extension point and the one worth copying into other stacks. Remember to extend reset() too if you add cached state.
Asserting the SHA in tests. If the collector exposes the hash as public data, a smoke test can assert the running app reports the expected commit. Useful in acceptance suites that run against a deployed preview.
Porting the idea to Laravel. There is no Laravel equivalent here, and none is needed: register a singleton that shells out to git rev-parse --short HEAD once, expose it via a view composer into your debugbar/telescope-adjacent layout, and add it to the footer of the dev layout. Ten lines. See barryvdh/laravel-debugbar collectors if you want it inside the debugbar panel rather than as a footer badge.
reset() implemented in GitDataCollector, PR #20) is the first change since the 2018 release, which is a real signal that the package is alive and that someone is running it on a current Symfony. It resolves the DataCollectorInterface::reset() requirement introduced in Symfony 4.4/5.x, so the "fatal on modern Symfony" scenario is much less likely than it was. Confirm the rest of the interface still matches your version, though: getName() was dropped from the interface in Symfony 5.0 and the toolbar template API moved to TemplateAwareDataCollectorInterface. Check those two before dropping it into a Symfony 6 app.reset() is a correctness fix, not a feature. Do not expect visible changes in the panel. Its value shows up in long-running workers and in test suites that boot the kernel repeatedly — if you had intermittent stale/blank commit data there, this is the fix.^1.4.5), diff GitDataCollector against what you had, and run your dev environment once before trusting it.exec() may be disabled. Shared hosting, hardened PHP-FPM pools, and some Docker base images ship disable_functions=exec,shell_exec. The panel will render empty or log a warning. Check php -i | grep disable_functions before blaming the bundle..git absence is a silent failure. Build artifacts, git archive exports, and vendor-split deploys have no repository, so git log returns a fatal error. The bundle generally degrades to an empty panel rather than throwing — which is fine, but don't treat a blank panel as a config bug.www-data against a repo owned by another user triggers detected dubious ownership in repository on modern git and returns nothing. Either git config --global --add safe.directory for the web user or run the collector as the owner.cache:clear is mandatory or you will keep seeing stale markup. Container rebuilds after a composer update that changed the collector's constructor are equally mandatory.dev only. Registered in prod, it exposes your commit metadata and remote URL to anyone who can reach the profiler, and the profiler is far easier to accidentally leave open than people assume. Gate the bundle registration on the environment, not on a config flag you might flip.reset() in place the bundle is now a good copy-paste source for Symfony 5+, but if you need this in Laravel or want env-var-driven SHAs, writing the twenty-line version yourself remains cheaper than depending on it.How can I help you explore Laravel packages today?