Author: Randall S. Becker, T1198 (NSGit) Chief Architect
Challenge
Managing releases has become increasingly complex as audit obligations and operational complexity continue to grow. Running full regression cycles across entire legacy applications is often impractical, and funding for comprehensive test automation is difficult to secure. As a result, teams need a reliable way to release small subsets of components selectively, execute partial installation scripts, and preserve evidence of origin for long-term audit history.
Provisioning
The core technical requirements for release management can be grouped into six practical areas:
| Area | Requirement |
| Repository and traceability | Maintain a repository that preserves relationships between source files and generated objects for audit purposes. |
| Release integrity | Reliably preserve immutable relationships among related release components to prevent missing or mismatched parts in complex releases. |
| Release identification | Clearly identify what is being released and where it will be deployed. |
| Approval and authorization | Provide an approval mechanism that authorizes proper release deployment. |
| Testing standards | Define sufficient test requirements to satisfy corporate security and integrity standards. |
| Deployment verification | Verify that only the designated, tested elements are included in the release. |
Release and Repository Structure
Git and NSGit make a very solid pairing for building and maintaining release repositories based on how revisions are stored in their repositories.
Separate Source and Release Repositories
A release repository structure should generally separate two concerns: source code management and release management. Keeping these repositories separate makes history easier to manage and prevents release artifacts from overwhelming the source repository.
Although it is possible to maintain full release history in a source repository, it is usually impractical. Installed objects often require separate retention rules, and storing built objects alongside source code can make the repository difficult to navigate and maintain.
Organize Release Components for Deployment
Release components should be maintained in distinct folders or sub-volumes. This structure allows individual components to be installed independently without requiring source code to be present in production during normal operations.
For audit and defect analysis, the release process should also make it easy to trace or restore the exact source version associated with a deployed product object.
- Keep source code and built release artifacts in separate repositories.
- Store release components in isolated deployment-ready folders or sub-volumes.
- Retain the ability to identify the source version behind each deployed object.
- Have a mechanism of identifying and selecting only those objects that are being released.
Identifying the Release
The global long-term identification of a release is usually done with a signed git tag in both repositories on the same commit. This permanent identification does not require clean-up and can act as a permanent record of what is to be built for the release. The advantage of a signature is to ensure that no hacking of the repository content is done following the construction of a release.
In the release repository additional structures are present. Specifically, there would be
- a branch representing the major release
- a tag representing the commit to deploy
- branches for fix releases (optional)
- a tag for the emergency fix to deploy
- in some situations, a tag for the fallback release to deploy if the original release fails.
While there are many ways of organizing the branches in an object repository, the most useful branch structure allows easy cleanup operations when the release is no longer needed – often governed by your retention policy. Another consideration is whether the emergency fixes are subject to the same retention policy or whether they can be removed with the main release.
The above example uses only a main release branch, release/2026-2, which contains the main release, 2026-2.0, and the emergency release 2026-2.1. The commits to deploy are tagged as deploy-2026-2.0 and deploy-2026-2.1, which represent the changes for the release (new, adds, deletes) objects and other artifacts.
It is important to note that commits that are part of emergency releases should either be cherry-picked or merge squashed into the release branch – also shown above – and tagged as fallback items referring to the prior release being reinstalled.
Releases can and should generally include more than just objects. They can include scripts and configuration file changes for products from numerous vendors and applications.
Quantifying Fallback Releases
There is a simple mechanism for constructing a fallback release in git. After a release is built, committed, and tagged creating a fallback release is generally as simple as using a revert operation to create a commit that undoes the release commit. These are represented in the above log by the fallback tags.
Using the revert operation makes the changes to the production environment cleaner than re-installing the old release or restoring from a backup as all changes, including undoing additions, are accounted for.
Managing Testing Environments
Managing testing is partly a matter of judgment and will only be covered briefly in this article. The key trade-off is balancing the cost of testing against what can reasonably be tested. Testing environments can also be structured in different ways. The author’s preferred approach is to build and test the release in a form that matches what will later be installed in testing and production environments.
A practical approach is to run as many tests as possible before approving git Pull Requests. This helps catch defects early, but it depends on automated test suites and can consume significant computing resources.
Following any Pull Request testing, a good practice is to run a full regression test cycle after the release is built or only run manual tests on the specific release components if a full cycle cannot be done.
Deploying to Staging and Production
Procedurally, deploying a commit to staging and production are similar. In both cases, the operation typically uses NSGit with a sparse checkout configuration only including the release directory in OSS and sub-volume in Guardian. In addition, checking out only the release commit, by tag, with a depth of 1 reduces the amount of work required as only the release commit is involved instead of all of history, which can be substantial in the release repository. A depth of 1 also omits the parent source commit(s) on which the release is built in both main release and emergency release situations.
When using a staging area, the clone and paired sub-volumes – an NSGit concept – point to an area on disk that is different from the production sub-volumes. A separate copy or rename step should be introduced, either manually or using NSGit workflows to move contents into production. The move can be facilitated by NSGit to display whether files are added, removed, or modified. The creation of this script is left as an exercise but is straightforward in TACL. When staging, using clean the working area before the checkout can reduce disk space.
When using a production area, simply clone to the production sub-volumes and use checkout as with staging. No extra copy is required.
In both cases, automating the steps is much easier and more reliable than doing any of this manually.
In Place Debugging and Problem Solving
If the production environment requires emergency diagnosis involving source code, the repository can be used to populate the source code sub-volumes by deepening the repository history to obtain the source commits, and disabling the sparse-checkout configuration, to move the source code into Guardian. To remove the source code when finished, you can set the depth back to 1 in nsgit fetch (or retain it until the next release) and restore the sparse-checkout configuration to have nsgit remove the source sub-volumes.
Maintaining Your Audit Environment
Maintaining the audit environment is similar to supporting in-place debugging, but it does not usually require changing the sparse configuration. In most cases, a file identity check can identify the origin of production files without requiring those files to be present in OSS.
Cleaning Up Old Release Code
With the release repository structure in place as described above, cleaning up old releases is simple. Simple delete the main release branch, or the emergency release branches if you are using them. This deletes
- the commits associated with the releases,
- the objects associated with each commit,
- the deployment and fallback tags,
What this cleanup retains is the entire commit history along the source code branches. Retention of source code is maintained indefinitely, unless you choose to rewrite your history a rebase, which can compress your history if needed.
Conclusion
Managing releases in a strong repository, such as NSGit and git, provide numerous benefits.
- All changes that are part of a release are explicitly known, documented, and tested through a consistent deployment process.
- Automation of release deploy and fallback operations improve deployment speed and reliability.
- Knowing exactly what must be deployed reduces that number and length of all hands calls to figure out what has to be done in an emergency in production.
- Audits are simplified by always knowing what is installed and how content was created. The relationship between sources and objects are always known and available in the production environment.
- Security is increased by not having to do manual file copies from development.
Glossary
Git and nsgit can be used interchangeably for most operations discussed here.
checkout is a git mechanism to make a specific version available in an environment.
clean is a git function that removes unneeded content from a working index and Guardian sub-volumes.
clone is a git operation to obtain content from a central repository management system.
deepen is a git fetch option that increases the size of history in your local repository to include entries that may have been ignored by previous clones or fetches.
depth is a git fetch and clone option that controls the size of history. In releases, a depth of 1 is standard practice.
file-identity is an NSGit-specific command that searches a repository for matching file signatures.
rebase is the git command used to rewrite history.
revert is a git command that creates a new commit to under a specific set of commits. This is useful for creating fallback releases.
sparse-checkout configurations in git control which directories are included in a clone or checkout.




Be the first to comment