Sometimes it is useful for an app to know which Git branch it was built from.
You might want to show the branch name in a debug screen, a title area, a footer, a settings page, a diagnostics panel, or somewhere else entirely. The exact placement belongs to the app. The build metadata system should only make the value available.
The general pattern is:
- Detect the Git branch during the build.
- Store it in generated build metadata.
- Read that metadata from the app.
- Expose it as a variable, function, or small provider.
- Render it wherever your UI wants.
The app should not need to run Git at runtime. Git is a build-time concern. Your UI should only receive a string.
At runtime, your app may not be running inside a Git repository.
A production app, a preview build, a test artifact, a desktop bundle, a mobile app, or a deployed web app may have no .git directory nearby. Even if Git is installed on the developer's machine, it may not exist in the runtime environment.
Build time is different. Your build system usually has access to the source checkout, so that is the right moment to ask Git for the current branch.
git branch --show-currentThen you embed the result into the app as build metadata.
The exact file format does not matter. You can use JSON, generated source code, environment variables, constants, plist files, properties files, or whatever your platform already prefers.
A portable approach is to generate a small JSON file:
set -euo pipefail
OUTPUT_FILE="${BUILD_INFO_OUTPUT:-./build-info.json}"
SOURCE_REPO_ROOT="${SOURCE_REPO_ROOT:-.}"
BRANCH_NAME="$(git -C "$SOURCE_REPO_ROOT" branch --show-current 2>/dev/null || true)"
if [ -z "$BRANCH_NAME" ]; then
SHORT_HASH="$(git -C "$SOURCE_REPO_ROOT" rev-parse --short HEAD 2>/dev/null || true)"
if [ -n "$SHORT_HASH" ]; then
BRANCH_NAME="detached-$SHORT_HASH"
fi
fi
mkdir -p "$(dirname "$OUTPUT_FILE")"
JSON_BRANCH_NAME="$(printf '%s' "$BRANCH_NAME" | sed 's/\\/\\\\/g; s/"/\\"/g')"
cat > "$OUTPUT_FILE" <<EOF
{
"sourceBranchName": "$JSON_BRANCH_NAME"
}
EOFYour build system can run this before packaging or compiling the app.
For example, the output might be:
{
"sourceBranchName": "feature/new-review-ui"
}If the build is running from a detached commit, the fallback might be:
{
"sourceBranchName": "detached-a1b2c3d"
}Once the metadata exists, load it using whatever configuration mechanism your platform already uses.
In generic pseudocode:
type BuildInfo = {
sourceBranchName?: string;
};
function getSourceBranchName(buildInfo: BuildInfo): string | undefined {
const branchName = buildInfo.sourceBranchName?.trim();
return branchName ? branchName : undefined;
}The important thing is that the rest of the app receives a simple value:
const sourceBranchName = getSourceBranchName(buildInfo);At this point, the app does not care how the branch name was produced. It could have come from JSON, native app metadata, an injected constant, or a server-rendered config object.
For UI code, hide the source of the value behind a tiny accessor.
function sourceBranchName(): string | undefined {
return getSourceBranchName(buildInfo);
}This keeps the UI flexible. The reusable branch-name UI does not need to know whether the branch came from JSON, native app metadata, an injected constant, or a server-rendered config object.
Once the app can read the branch name, placement is just a product decision.
You can create a small reusable view or component that owns the lookup and only renders the branch text. In SwiftUI, that might look like this:
import Foundation
import SwiftUI
struct BuildInfo: Decodable {
private let rawSourceBranchName: String?
static var sourceBranchName: String? {
guard let buildInfo = BuildInfo.current else {
return nil
}
let branchName = buildInfo.rawSourceBranchName?.trimmingCharacters(in: .whitespacesAndNewlines)
return branchName?.isEmpty == false ? branchName : nil
}
private enum CodingKeys: String, CodingKey {
case rawSourceBranchName = "sourceBranchName"
}
}
private extension BuildInfo {
static var current: BuildInfo? {
guard let url = Bundle.main.url(forResource: "BuildInfo", withExtension: "json"),
let data = try? Data(contentsOf: url)
else {
return nil
}
return try? JSONDecoder().decode(BuildInfo.self, from: data)
}
}
struct BranchNameView: View {
var body: some View {
if let branchName = BuildInfo.sourceBranchName {
Text(branchName)
}
}
}
#Preview {
BranchNameView()
}That view does not know where it will be used. It can live in a title area, a navigation subtitle, a debug footer, a settings screen, a developer menu, or a diagnostics page.
The same idea works in any UI framework. The reusable UI can own the lookup:
function BranchNameView() {
const branchName = sourceBranchName();
if (!branchName) {
return null;
}
return <span>{branchName}</span>;
}Then use it wherever it makes sense:
<BranchNameView />Branch names are most useful in development, staging, QA, or internal builds. You may not want them in production builds.
There are two common ways to handle that. You can hide the UI:
const shouldShowSourceBranch = appEnvironment !== "production";{shouldShowSourceBranch && (
<BranchNameView />
)}Or you can omit the metadata entirely from production builds. In that case, sourceBranchName is simply unavailable and the UI renders nothing.
The reusable architecture is small:
type BuildInfo = {
sourceBranchName?: string;
};
function getSourceBranchName(buildInfo: BuildInfo): string | undefined {
const branchName = buildInfo.sourceBranchName?.trim();
return branchName ? branchName : undefined;
}
function sourceBranchName(): string | undefined {
return getSourceBranchName(buildInfo);
}And usage stays deliberately simple:
<BranchNameView />Then place BranchNameView wherever the branch should appear.
The branch name should be detected by the build, not by the running app.
Once detected, treat it like any other build metadata: version number, commit hash, build date, environment name, or feature flag set. Store it in a platform-appropriate place, expose it through a small function, and let a reusable branch-name UI decide how to render it.
That keeps the feature portable across platforms and avoids tying the implementation to any specific title bar, navigation system, window API, or UI framework.