← AZ-400: DevOps from delivery to operations
22 / 26 · 110 MIN

Repositories: parallel work and reproducible recovery

Distinguish local state, shared references and required dependencies when recovering a hotfix with worktrees, bundles and submodules.

Separate urgent work without losing context

An urgent correction should not force current work to mix with a hotfix. A linked worktree lets another branch be checked out in its own directory while preserving the original working area. Before creating it, confirm the starting commit and give the change an identifiable branch. Git normally refuses to check out one branch in two worktrees; overriding that safeguard does not create independent branches. In the exercise, main contains the base version and hotfix receives a service.txt change. The team can compare both contexts without recording the urgent change on main. Record each area’s location and state so the next person does not run commands in the wrong directory. File isolation does not replace review, testing or deployment authorization.

Know what is separate and what is shared

Each worktree has its own HEAD, index and working files, but linked worktrees belong to the same repository. The lab stages service.txt in hotfix and confirms that main’s index retains base. It then creates a commit and resolves the hotfix reference from the primary area. This does not mean a merge occurred: main still points to the initial commit. The same reasoning prevents treating a linked directory as an independent backup. If the shared object database becomes unavailable, two directories do not establish two recoverable copies. For a team handover, explain this relationship explicitly and identify which references must be preserved. Use Git commands to inspect state rather than manually editing repository internals. Confirm both the checked-out branch and the proposed destination before changing references.

Preserve local state before cleanup

A commit does not automatically contain every visible file. There are staged changes, working-tree-only changes and untracked files. When worktree remove refuses a dirty area, investigate these categories before using a force option. A branch may preserve commits while leaving an investigation note unprotected. The exercise also demonstrates restore --staged: it removes the change from the index, using HEAD by default, while keeping the file edit. Adding --worktree changes the operation’s destination and can discard precisely the content intended for preservation. First confirm the desired result, inspect differences and preserve what is needed. A successful command does not prove the user selected the right state area. Ask whether the next commit, the current files or both should change before choosing flags.

Stash and reflog have local scope

Worktrees do not provide a private stash list for each directory. The refs/stash reference is shared; the newest entry may have been created in another area. Inspect content and identifier before applying an entry, and consider possible conflicts at the destination. The lab uses apply to retain the entry during observation; it does not show that any stash can be applied on any branch. Reflog records reference movements in the local repository. A fresh clone does not inherit movements that existed only on someone else’s laptop. If an important commit has become unreferenced, seek available evidence and preserve located objects before cleanup. Do not promise recovery of content never made available outside an inaccessible machine. Local evidence has value, but its availability must be confirmed.

Verify bundle prerequisites

A bundle can carry history reachable from selected references. An incremental set excludes a base that its recipient must possess. In the lab, base..main verifies in a repository containing base and fails in an empty one. The difference does not establish corruption: it shows that destinations have different conditions. Recovery starts with a self-contained base bundle and then applies the increment. When cloning the exercise bundle, select main explicitly; do not depend on inference of an initial branch that the package may not advertise as HEAD. Record included references and prerequisites external to the package. File size, extension and successful creation do not replace a rehearsal in the intended destination type. Distinguish a valid incremental transfer from a complete recovery package.

Pin and obtain submodule dependencies

A superproject identifies the expected submodule commit through a gitlink. Publishing that pointer is not the same as making every dependent repository object available. In the second exercise, the library has an approved version A and later advances to B; the superproject still requests A. A normal submodule checkout uses A and leaves HEAD detached without that being an error. For a tree with nested dependencies, handle initialization and recursive updates explicitly. Also confirm access for the user or agent performing recovery. Do not silently replace an unavailable commit with the latest one, because that changes the content being built. The lab uses separate local repositories; it neither combines submodules with worktrees nor validates remote-server permissions. Availability and pin correctness are separate checks.

Run and interpret the lab

The supplied script creates temporary repositories and uses only local transport. It disables hooks, signing and global configuration to avoid dependencies on the learner’s machine; finally it removes only its temporary directory. Checks observe separate indexes, shared references, refusal to remove a dirty worktree, unstaging without losing edits, shared stash, bundle recovery and pinned submodule checkout. Read each check name and connect its result to the claim it supports. The recursive command test does not construct a nested hierarchy: that capability is supported by documentation and should be exercised separately if actual acceptance depends on it. Do not turn local results into guarantees about network latency, remote authorization, hosting policies or storage availability. Record the Git version used when comparing results across machines.

Decide whether the hotfix can be handed over

In the fictional case, RUN receives an incremental bundle for an empty repository, while an operational note remains only on its author’s machine. Readiness has two distinct gaps: insufficient history and parameters still needing preservation and review. Obtain an approved base or a self-contained package; retain the note through an authorized method and confirm procedure version. Then reproduce the correction in the intended rehearsal environment, checking identifiers and required dependency content. An alternative can satisfy the same criterion through another method, but must demonstrate that before intervention. If no approved exception exists and reproduction fails, keep delivery pending. This rule belongs to the scenario; the course neither invents a bank’s internal procedure nor turns a Git exercise into production approval. Record residual uncertainty for the decision owner.

"""Original local Git exercise. Creates and removes only its own temporary directory.
No network, credentials, user configuration, production repository or Git hosting API.
"""
import json
import os
import subprocess
import tempfile
from pathlib import Path

checks = []
def check(name, condition):
 if not condition:
 raise AssertionError(name)
 checks.append(name)

env = {k: v for k, v in os.environ.items if not k.startswith('GIT_')}
env.update(GIT_CONFIG_NOSYSTEM='1', GIT_CONFIG_GLOBAL=os.devnull,
 GIT_TERMINAL_PROMPT='0', GIT_ALLOW_PROTOCOL='file', LC_ALL='C')

with tempfile.TemporaryDirectory(prefix='dr-az400-recovery-') as directory:
 root = Path(directory)
 hooks = root / 'empty-hooks'
 hooks.mkdir
 def git(repo, *args, ok=True):
 result = subprocess.run(['git', '-c', 'user.name=DR local exercise',
 '-c', 'user.email=exercise@example.invalid', '-c', 'commit.gpgsign=false',
 '-c', 'tag.gpgsign=false', '-c', 'core.hooksPath=' + str(hooks),
 '-c', 'gc.auto=0', '-c', 'maintenance.auto=false', '-C', str(repo), *map(str, args)],
 env=env, text=True, capture_output=True, timeout=20)
 if ok and result.returncode:
 raise AssertionError('Local Git command failed: ' + ' '.join(map(str, args)) + '\n' + result.stderr)
 return result
 def init(name):
 repo = root / name
 repo.mkdir
 git(repo, 'init', '--template=' + str(hooks), '-b', 'main')
 return repo
 def commit(repo, filename, text, message):
 (repo / filename).write_text(text)
 git(repo, 'add', '--', filename)
 git(repo, 'commit', '-m', message)
 return git(repo, 'rev-parse', 'HEAD').stdout.strip

 main = init('main-repo')
 base = commit(main, 'service.txt', 'base\n', 'base release')
 linked = root / 'hotfix-worktree'
 git(main, 'worktree', 'add', '-b', 'hotfix', linked, 'HEAD')
 duplicate = git(main, 'worktree', 'add', root / 'duplicate', 'main', ok=False)
 check('same checked-out branch refused by default', duplicate.returncode!= 0)
 (linked / 'service.txt').write_text('hotfix draft\n')
 git(linked, 'add', 'service.txt')
 check('linked index contains draft', git(linked, 'show', ':service.txt').stdout == 'hotfix draft\n')
 check('main index remains independent', git(main, 'show', ':service.txt').stdout == 'base\n')
 check('main working tree remains independent', (main / 'service.txt').read_text == 'base\n')
 check('main HEAD remains main', git(main, 'symbolic-ref', '--short', 'HEAD').stdout.strip == 'main')
 check('linked HEAD is hotfix', git(linked, 'symbolic-ref', '--short', 'HEAD').stdout.strip == 'hotfix')
 removal = git(main, 'worktree', 'remove', linked, ok=False)
 check('dirty worktree removal refused', removal.returncode!= 0 and linked.exists)
 git(linked, 'restore', '--staged', '--', 'service.txt')
 check('restore staged resets only index', git(linked, 'show', ':service.txt').stdout == 'base\n')
 check('restore staged retains working file', (linked / 'service.txt').read_text == 'hotfix draft\n')
 git(linked, 'stash', 'push', '-m', 'hotfix draft for inspection')
 stash_oid = git(linked, 'rev-parse', 'refs/stash').stdout.strip
 check('stash ref shared across linked worktrees', git(main, 'rev-parse', 'refs/stash').stdout.strip == stash_oid)
 git(linked, 'stash', 'apply', stash_oid)
 git(linked, 'add', 'service.txt')
 git(linked, 'commit', '-m', 'reviewable hotfix candidate')
 hotfix = git(linked, 'rev-parse', 'HEAD').stdout.strip
 check('branch ref shared across worktrees', git(main, 'rev-parse', 'hotfix').stdout.strip == hotfix)
 check('main branch not moved by linked commit', git(main, 'rev-parse', 'main').stdout.strip == base)
 check('main reflog lacks linked HEAD checkout event', 'moving from' not in git(main, 'reflog', 'show', 'HEAD').stdout)

 full = root / 'base.bundle'
 git(main, 'bundle', 'create', full, 'main')
 later = commit(main, 'service.txt', 'release two\n', 'second release')
 (main / 'draft.txt').write_text('not committed\n')
 increment = root / 'increment.bundle'
 git(main, 'bundle', 'create', increment, base + '..main')
 empty = init('empty-recipient')
 check('increment needs prerequisite history', git(empty, 'bundle', 'verify', increment, ok=False).returncode!= 0)
 restored = root / 'restored'
 git(root, 'clone', '--branch', 'main', full, restored)
 check('full bundle restores committed base', git(restored, 'rev-parse', 'HEAD').stdout.strip == base)
 check('recipient with base verifies increment', git(restored, 'bundle', 'verify', increment).returncode == 0)
 git(restored, 'fetch', increment, 'refs/heads/main:refs/heads/recovered')
 check('increment restores expected commit', git(restored, 'rev-parse', 'recovered').stdout.strip == later)
 check('increment restores expected file', git(restored, 'show', 'recovered:service.txt').stdout == 'release two\n')
 check('uncommitted file absent from bundle history', git(restored, 'cat-file', '-e', 'recovered:draft.txt', ok=False).returncode!= 0)
 check('source draft remains intact', (main / 'draft.txt').read_text == 'not committed\n')
 check('reflog entries not transported by bundle', 'second release' not in git(restored, 'reflog', 'show', 'HEAD').stdout)

 # Separate repositories: the exercise does not combine submodules with worktrees.
 library = init('local-library')
 lib_one = commit(library, 'rule.txt', 'approved rule\n', 'library one')
 parent = init('superproject')
 commit(parent, 'README.txt', 'fictional adapter\n', 'adapter base')
 git(parent, 'submodule', 'add', library, 'rules')
 git(parent, 'commit', '-m', 'pin approved rule')
 lib_two = commit(library, 'rule.txt', 'new unapproved rule\n', 'library two')
 consumer = root / 'consumer'
 git(root, 'clone', parent, consumer)
 check('ordinary parent clone does not populate submodule', not (consumer / 'rules/rule.txt').exists)
 git(consumer, 'submodule', 'update', '--init', '--recursive', '--checkout')
 check('submodule uses recorded commit, not remote head', git(consumer / 'rules', 'rev-parse', 'HEAD').stdout.strip == lib_one)
 check('newer source commit is distinct', lib_two!= lib_one)
 check('default checkout leaves submodule detached', git(consumer / 'rules', 'symbolic-ref', '-q', 'HEAD', ok=False).returncode!= 0)
 check('submodule content matches approved pin', (consumer / 'rules/rule.txt').read_text == 'approved rule\n')
 git(main, 'worktree', 'remove', linked)
 check('clean linked worktree can be removed', not linked.exists)
 check('removing linked worktree preserves branch commit', git(main, 'rev-parse', 'hotfix').stdout.strip == hotfix)
 version = git(root, '--version').stdout.strip

print(json.dumps({'passed': True, 'gitVersion': version, 'checks': len(checks),
 'checkNames': checks, 'networkUsed': False,
 'temporaryDirectoryRemoved': not Path(directory).exists,
 'limitations': 'Local Git only. No remote authorization, hosting policy, cloud pipeline, storage-loss simulation or cross-version execution.'}, indent=2))
IN PRACTICE

A base..hotfix bundle reaches RUN without base history or an untracked parameter note. Completing the package and rehearsing recovery are distinct from successfully creating the file.

Common pitfalls

Treating worktrees as independent clones, stash as private to a directory, an incremental bundle as a complete backup and a gitlink as transport for its dependent repository.

Related topics: Hotfix review and backporting · Dependency backup and recovery · Clean build agents

Take this idea with you

Recovery requires content and prerequisites available at the destination. Confirm which state each command changes and demonstrate reconstruction of the intended version.

Create account

Reference: git-worktree manual · AZ-400 objectives 2026-07-27

Microsoft is a trademark of the Microsoft group of companies. bigsavant.com is an independent preparation platform and is not affiliated with, associated with, sponsored, authorised or endorsed by Microsoft. Content and questions are original, are not official exam questions, and completing our tests does not award or guarantee any certification. Names are used only to identify the subject. All other trademarks belong to their respective owners.