This guide explains how DirXMLDevWeb keeps a driver set in git. It recommends a way to work:
- with the dev, test and production vaults of one driver set;
- when to start a branch;
- how a change gets back into
mainand from there into production; - where the repository lives.
It assumes you have done the tutorial: you have a project, a vault, and you have committed and deployed once.
The screenshots use a synthetic driver set, SynthSet, with one Loopback driver. Alice is an editor, and Bob a deployer.
- One driver set is one project, with one repository and one tree. Dev, test and production are environments, the vaults that same tree deploys to. They are not branches and not copies.
- What differs between environments goes in the environments' overrides: GCVs, shim settings, the shim's server and account. Passwords go in each environment's secrets. None of this goes in a branch.
- A change starts on a branch of its own. Deploy it to dev from there, as often as you like.
- Merge it into
main.- On GitHub or GitLab, use a pull request.
- Otherwise, use Merge into main… on the bar.
- Promote from
main: deploy the same content to test, then to production. Production takes only whatmainholds on the repository, and only once test has had exactly that content.
Every project has a repository and a base branch, usually main. New branches start from it,
and pull requests and merges go into it. Projects → Repository shows both.
When you open a branch, the server makes a checkout of it for you alone:
data/workspaces/<project>/<you>/<branch>
Everything the page edits, validates, compares and deploys is that checkout's files. Two people on the same branch each have their own copy. They see each other's work only once it is pushed, and then only after they bring it in.
A project used in place (a checkout on the server's host, not a repository) is the exception: it is one person's working copy, and the page lets only that person open it.
From left to right:
| On the bar | What it does |
|---|---|
| the project, the branch, switch | switch lists Your branches (on this server, pushed or not) and On the repository. new branch… starts one from the base branch, as the repository has it now. |
| ● N uncommitted files / nothing to commit | What differs from your last commit. Choose it to see the files. |
| ↑ N to push | Commits you made that the repository doesn't have yet. |
| vs main | What your branch carries that main doesn't: what a merge would bring in. |
| Commit, Commit & push, Push | Commit records on your copy only. Push sends your commits to the repository. |
| Update from main | Brings what was merged into main since you started into your branch. |
| Merge into main… | Merges your branch into main on the repository (see section 5). Shown when the repository is not on GitHub or GitLab. |
| Create pull request… / #12 open ↗ | On GitHub or GitLab: opens the request, or links to it. |
| Get latest main | On main itself: brings in what others merged since your copy was taken. |
| Path in the tree | What it is | Committed? |
|---|---|---|
drivers/, library/, driverset.xml, … |
The driver set itself | yes |
overrides/<environment>.properties |
Values that differ per environment | yes |
deploy-log/<environment>.jsonl |
One line per deploy: when, who, which commit, the outcome | yes, through Commit the deploy record |
deploy-snapshots/<environment>/ |
What each deploy was about to change, for rollback | never |
.gitattributes |
Keeps vault content byte for byte, and merges deploy logs line by line | yes |
Vault addresses, sign-ins and driver secrets are never in the repository. They live in the server's environment files and each person's own file (Connections).
In Connections, add a vault for each environment of the driver set, each with its kind:
| Name (yours to choose) | Kind | The file says |
|---|---|---|
dev |
dev | dev.tier=dev |
test |
test | test.tier=stg |
prod |
production | prod.tier=prd |
Then, on the production vault, open Edit → More settings and set Deploy order: deploy to …
first to test. The file then says prod.requires=test.
On a server with more than one driver set, give the project its own environments with
project.<name>.environments=dev,test,prod. Then no one deploys this tree to another driver set's
vaults.
The tree holds one base value for every GCV and setting. When a value must differ in an environment, add an override for it. Open Env overrides in the outline, at the top of the driver set:
A deploy to test applies the test column over the base value, and a deploy to prod applies
the prod column. An empty cell means the environment takes the base value.
Validation warns when a key is set for one environment but not another (override-missing-env).
It is usually a value someone forgot.
Keys can name:
- a driver's GCVs, shim settings and engine control values;
- the shim's server and account (
shim-auth-server,shim-auth-id); - the driver set's GCVs.
DirXMLDev's vault-deploy.md lists the keys.
Passwords (the shim's, the Remote Loader's, named passwords) go in each environment's driver secrets, under Connections → Edit. They are never committed.
Every deploy, of every kind, needs:
- the deployer role;
- the environment's name typed out;
- no validation errors in what it deploys.
On top of that:
| Kind | From which branch | Also |
|---|---|---|
| dev | any | — |
| test | any (recommended: main) |
— |
| production | main, as the repository has it: all your commits pushed, and nothing merged since that your copy lacks. Any branch when the vault allows it (only one environment). |
Everything committed. A green deploy to test of the same content. Production's current state known: the vault must match the last production deploy, or you capture its state first (the plan offers it). |
"The same content" means the commit you deployed to test, or any commit that differs from it only in
deploy-log/ and deploy-snapshots/. So committing the test deploy's record does not make
production ask for another test deploy. Any other change since does.
The production check does not run the simulator. If the tree has simulator cases, run them before you promote.
Some driver sets have a production vault and nothing else, so there is nowhere to try a change
before it reaches production. Leave Deploy order empty: "deploy to test first" applies only
when it is set. Then, on the production vault, open Edit → More settings and tick deploy
from any branch, not only main. The file then says prod.fromAnyBranch=true.
Production then takes a deploy from your branch, so you can deploy the change, check it and merge it afterwards. Production still needs:
- everything committed;
- the environment's name typed out;
- production's state known.
Only a deployer can change the setting. Untick it once the project has a dev or test vault.
Choose switch → new branch…, give it a name and choose Open. Name the branch for the change,
for example feature/veto-surname or fix/ad-group-sync. The branch starts from main as the
repository holds it now.
Edit with any view. Each write goes through review. Then type a commit message and choose Commit & push. Pushing early keeps your work safe on the repository and lets others see it.
Go to Vault → Compare & deploy. Compare with dev, plan, type dev and deploy. Repeat until
it works.
After each deploy, Commit the deploy record, then Push. The record travels with the branch and is merged with it.
When others merge into main, choose Update from main. Doing it before you merge means you
test your change together with theirs.
If the update conflicts, nothing changes in your copy until you settle the conflicts in the page: see Resolving a conflict.
See section 5.
-
Open
main: switch → main, or Open main after a merge. If you already had it open, choose Get latest main. -
Deploy to
test, then choose Commit the deploy record and Push. -
Test it in the test environment.
-
Compare and plan against
prod. The plan checks what section 2 lists before you type anything. When something stands in the way, the plan says so, with the way on:The plan says Do this Production deploys come from main Merge the branch, then open main.Push first Choose Push in the panel. Get the latest main first Choose Get latest main in the panel. Production would otherwise get something other than what mainholds.prod takes only what test has had mainchanged since the test deploy. Deploy it to test again.prod's current state isn't on record (a first deploy) or prod changed since the last deploy on record (someone changed it outside the tree) Choose Capture prod's current state. It reads the vault and records what it holds; nothing is written to the vault. Then follow the steps it shows: Merge it into this branch (when it found changes), Commit and push the record, Compare again, and plan again. -
Type
prodand deploy. -
Commit the deploy record and Push, so the repository records what production runs.
Start a new branch for each change you would deploy, or roll back, as one. Usually that is
one ticket. Keep it short-lived: merge it within days, not weeks. The longer a branch lives, the
more main moves under it and the harder the merge.
| Situation | Do this |
|---|---|
| Any change that will reach production | A new branch from main. |
| Trying something out | A branch. If you drop it, nothing reaches main. |
| An urgent fix to production | A new branch from main, merged as soon as it works in dev, then the same test-then-production path. There is no shortcut around test: production refuses content test hasn't had. |
| Two changes at once | Two branches. Each merges and promotes on its own. |
| Your branch was merged and you start something else | A new branch, which starts from the latest main. Don't keep using the merged one. |
You only need to commit a deploy record on main |
Commit it on main. That is what main is for. |
Don't:
- Keep a branch per environment (
dev,test,prodbranches). Environments are vaults, and the differences between them are overrides. Branches per environment drift apart, and "what is in production" stops having one answer. - Keep one long-lived personal branch for everything.
- Edit
maindirectly for changes to the driver set. They skip review, and they skip dev.
The page also makes branches you don't start yourself. A production deploy run with capture
drift records the vault's actual state on a branch as-found/<environment>/<time>. Merge it into
this branch, under the deploy's result, puts your change on top of it. It isn't a branch to work on.
When the project's repository is on GitHub or GitLab, the bar offers Create pull request… (GitLab: merge request). The request is reviewed and merged on GitHub or GitLab, where your branch protection, required reviews and checks apply.
The page creates the request with your own Git token, or else with the project's. Add yours under Projects → Your Git tokens. pull-requests.md has the details.
Before you merge it there, choose Update from main on the branch and deploy it to dev once
more. You then merge exactly what you tested, and production's "same content as test" check will
pass once main has been deployed to test.
When the repository is one the server made, or plain git with no provider in front of it, the bar
offers Merge into main…. Any editor can use it. It is shown on a branch that isn't main, once
everything is committed.
The dialog shows the commits and files the branch would bring in. It warns when main has commits
your branch doesn't:
The warning means the merge would put your change and someone else's together, and no
environment has run that combination. You can merge anyway; then deploy main to test before
production, which production requires regardless. The safer path is the one the dialog names:
- Cancel.
- Update from main.
- Deploy to dev or test.
- Merge.
Merge into main then:
- pushes your branch;
- merges it into the repository's
mainas a merge commit in your name; - pushes
main.
Your own copy stays on your branch. Open main takes you to main to promote the change.
Open main and delete does the same and deletes the branch you merged: every commit of
it is in main now, so nothing is lost.
Merged branches pile up. Next to switch, choose merged branches….
- The list: every branch on the repository whose every commit is already in
main, with when and by whom it last changed. Tick the ones to delete and choose Delete. - Checked again: the server re-checks that each one is merged before deleting it, and never deletes the branch you have open.
- Your copy: your copy of a deleted branch on the server goes with it, unless it holds uncommitted work or commits the repository never had.
- Other people's copies: they are kept, and show as not on the repository.
On GitHub or GitLab, a merged pull request offers the same button, and the page leaves it to them.
If the branch conflicts with main, nothing is merged, and the dialog lists the files. Resolve
the conflicts on your branch (below), then merge again. If someone else merged in the same
moment, the push is refused and nothing changes; merge again.
A project used in place has no merge on the page: merge with git in that checkout.
Every branch that deployed has appended to deploy-log/<environment>.jsonl. Merging two of them
keeps both sides' lines, in every checkout the page makes. A new import's .gitattributes says so
too, for the CLI and your own clones. GitHub and GitLab don't use that setting when they merge a
pull request. So choose Update from main before merging a request: the logs then merge in your
copy, where it does apply.
When your change and main touch the same lines, Update from main stops before merging anything
and the bar offers Resolve the conflicts…:
- The files: the dialog lists each conflicting file. Everything else
mainchanged merges on its own and comes along. - The two sides: for a file, the left side is
main's version, and the right side is the merge as it will be committed. Git marks each clash between<<<<<<<and>>>>>>>: your branch's lines first, thenmain's. - Settle each conflict: use Keep this branch's, Take main's or Keep both. You can also
edit the right side, or use the arrow beside a block to take
main's lines there. A file deleted on one side and changed on the other is kept or deleted whole. - Commit: when no marks are left, choose Commit the merge, with a message of your choice. The
server redoes the merge and commits exactly what you settled.
- It refuses if your branch or
mainmoved in the meantime; open the conflicts again. - It refuses if the tree wouldn't read.
- It refuses if your branch or
- Check and push: check the result with vs main and validation, then choose Push.
Nothing is left half-merged while the dialog is open: until you commit, your copy is exactly as it was. A captured production state that conflicts with your branch (Merge it into this branch, after a capture) is resolved the same way.
With git instead, in a shell in your checkout:
docker exec -it -w /var/lib/idmweb/workspaces/PROJECT/YOU/BRANCH idmweb shBRANCH is the branch name with / written as _. Then:
git fetch origin main && git merge FETCH_HEADEdit the conflicted files, then commit:
git add -A && git -c user.name="Your Name" -c user.email=you@example.com commit --no-editWhen you start a project from a vault and give it no repository, the server makes one:
data/projects/<project>.git, which is ~/idmweb/data/projects/ on a local install. It works,
and Merge into main… merges into it. But it has no reviews, and it lives only on that server's
disk. Archiving the project (Projects) keeps a copy, but moving it to a git host is better.
Projects → Repository → Push this project to a remote… (deployers):
- Make an empty repository on the host.
- Paste its address and, for HTTPS, a token that can push.
- Check, then type the project's name to confirm.
Every branch and tag is copied. The project and everyone's open copies then push there. The server's own repository is kept, renamed.
From then on, a repository on GitHub or GitLab merges through pull requests, and Merge into main… is no longer offered.
Change remote… points the project at the same repository under a new address, for example a renamed organization or a new host. It must hold the base branch.
| What | For | Where |
|---|---|---|
| The project token (HTTPS) or deploy key (SSH) | cloning, fetching and pushing, for everyone on the project | Projects → Repository, when you set the remote. Kept on the server, never in a URL or a log. |
| Your Git token | opening pull requests as yourself | Projects → Your Git tokens |
The repository is ordinary git. You can clone it and use the idm CLI.
Your commits reach the page once pushed, and the page's commits reach you when you pull. The same
production rule is available on the command line: idm vault.deploy tree --env prod --confirm prod --yes --base-branch main.
feature/x ──commit──▶ deploy to dev ──▶ Update from main ──▶ Merge into main / pull request
│
main ◀───────────────────────────────────────────────────────────────┘
│
├── Get latest main ──▶ deploy to test ──▶ commit the record, push
└──────────────────────────────────────▶ deploy to prod ──▶ commit the record, push
environment-practices.md lists the habits that keep QA and production comparable across this cycle: where a fix goes, what belongs in a GCV, naming, what only some environments get, and how to take on an existing system.





