Git / NetDevOps
Branches and Diffs: Reviewing a Change Before It Ships
What you'll learn
- A branch as an isolated place to propose a change
- Diffing a branch against trunk as the real review step
- A genuine, verified discovery about merging and the working tree
- How this connects to dry-run/compare_config()'s review-before-commit principle
Listen to a guided explanation of this lesson.
Spoken narration isn't supported in this browser.
A branch: propose a change without touching trunk
The same principle, a new mechanism
Reliable Automation's dry-run mode (Phase 2.8) and NAPALM's compare_config() (Phase 3.5) both taught: see what would change before committing to it. A branch is that same principle applied to history itself -- a proposed change lives somewhere separate from trunk until it's reviewed.
feature = repo.create_head("add-vlan-30")
feature.checkout()
with open("repo1/sw1.cfg", "a") as f:
f.write("vlan 30\n name MGMT\n")
repo.index.add(["sw1.cfg"])
repo.index.commit("Propose VLAN 30")create_head() makes a new branch pointing at the current commit. checkout() switches to it -- trunk (master) is completely untouched by anything committed here.
Diffing the branch against trunk: the real review step
trunk = repo.heads.master
print(repo.git.diff(f"{trunk.name}...{feature.name}"))The ... syntax diffs trunk against the branch's proposed changes -- exactly what a reviewer needs to see before approving a merge.
diff --git a/sw1.cfg b/sw1.cfg
index 3fa0852..74ba68a 100644
--- a/sw1.cfg
+++ b/sw1.cfg
@@ -3,3 +3,5 @@ interface GigabitEthernet0/1
description mgmt-uplink
vlan 20
name VOICE
+vlan 30
+ name MGMTReal, verified output. This is precisely what compare_config() (Phase 3.5) showed for one device's candidate config -- the same review-the-diff-first discipline, now applied to a proposed commit.
Merging: real, verified, and a genuine discovery
trunk.checkout()
merge_base = repo.merge_base(trunk, feature)
repo.index.merge_tree(feature, base=merge_base)
merge_commit = repo.index.commit(
"Merge add-vlan-30",
parent_commits=(trunk.commit, feature.commit),
)
with open("repo1/sw1.cfg") as f:
print(f.read())merge_tree() combines the two branches' changes into the index; commit() with two parent_commits creates a real merge commit.
A real, non-obvious discovery -- found by actually running it
The first run of this exact code showed the file STILL missing VLAN 30 after the merge commit succeeded. merge_tree() and commit() update the repository's history and its index -- but not the working-tree files on disk, until something explicitly syncs them. This is not documented prominently and was found only by checking the actual file content, not assumed from the method names.
hostname SW1
interface GigabitEthernet0/1
description mgmt-uplink
vlan 20
name VOICEReal, verified output -- VLAN 30 is genuinely missing here, despite the merge commit existing in history. This is the actual, first-attempt result, not a hypothetical.
The fix, verified
repo.head.reset(index=True, working_tree=True)
with open("repo1/sw1.cfg") as f:
print(f.read())reset() with working_tree=True explicitly syncs the on-disk files to match HEAD's current commit.
hostname SW1
interface GigabitEthernet0/1
description mgmt-uplink
vlan 20
name VOICE
vlan 30
name MGMTReal, verified output. VLAN 30 now genuinely present on disk -- the fix, confirmed by re-reading the file, not assumed to have worked.
Key takeaways
- A branch isolates a proposed change from trunk completely -- verified directly, nothing on master changes until an explicit merge.
- Diffing branch...trunk is the real review step -- the same principle as dry-run (Phase 2.8) and compare_config() (Phase 3.5), applied to a proposed commit instead of a device config.
- A genuine, verified discovery: merge_tree() + commit() update history and the index, but not the working-tree files on disk, until reset(working_tree=True) explicitly syncs them.
- This was found by actually reading the file after a 'successful' merge, not assumed from the method names -- the established verify-first discipline, applied to Git itself.