Today's two tasks are both about a new base. A feature branch that needs to sit on top of a master that has moved. A database instance that needs to come back from a snapshot taken when things were fine. In each case, the interesting question is the same: what carries over, and what do you have to say out loud?
One Git task, one AWS task. Rebase a feature branch onto master without creating a merge commit, then snapshot an RDS instance and restore it into a new one. The tasks come from the KodeKloud Engineer platform.
Rebase: not moving commits, replaying them
The requirement was specific, and the specificity is the lesson. A developer's feature branch was behind master. Bring it up to date without losing any feature work, and without a merge commit.
That second clause rules out git merge master. Merge joins two histories and records the join, which is the merge commit. Rebase does something else entirely.
cd /usr/src/kodekloudrepos/media
git branch
git log --oneline --graph --all --decorate
git checkout feature
git rebase master
git log --oneline --graph --decorate
Git's own documentation describes what happens under git rebase master: it lists the commits on your branch that are not on master, checks out master, and then replays each of your commits on top of it, one at a time, in a way it compares to running git cherry-pick for each one.
Replays. Not moves. Every commit that comes out the other side has a new hash, because a commit's identity includes its parent, and the parent is different now. Your work is preserved, the commits carrying it are not the same objects they were.
That is exactly why there is no merge commit. Rebase does not join two histories, it rewrites yours so it looks like it was always based on master's current tip. You get a straight line, at the cost of a history that is no longer a record of what actually happened.
Two things I had to be deliberate about.
Direction. Rebase applies to the branch you are standing on and takes the branch you name as the new base. git checkout feature then git rebase master means replay feature onto master. Run it the other way round, and you have rewritten master, which is a much worse afternoon.
Then the push:
git push --force-with-lease
History was rewritten, so a normal push is refused. --force-with-lease requires the remote ref to still match your remote-tracking branch and refuses if it does not, so it will not silently flatten something a colleague pushed while you were rebasing.
Worth reading the caveat in Git's own docs, though, because it is not the guarantee people assume. The lease is checked against your remote-tracking ref, so anything that quietly runs git fetch in the background, a cron job or an IDE, updates that ref and defeats the protection. --force-if-includes, which additionally requires the remote tip to be reachable from your branch's reflog, closes that gap.
The conflict experience also differs from merge in a way nobody warns you about. A merge conflicts once, with both complete sides in front of you. A rebase can conflict once per replayed commit, each time showing you one commit's worth of change against a base that has moved. More stops, smaller pieces, and git rebase --abort puts everything back exactly as it was if it stops being worth it.
And the rule that matters most, straight from the documentation: rebasing a branch that others have based work on forces everyone downstream to fix their history by hand. Rebase your own unshared branches freely. Leave shared ones alone.
The restore: you only type what you want to be different
The AWS task was four steps: wait for the source instance, snapshot it, restore the snapshot into a new instance, and force the new one to db.t3.micro.
aws rds wait db-instance-available --region us-east-1 \
--db-instance-identifier datacenter-rds
aws rds create-db-snapshot --region us-east-1 \
--db-instance-identifier datacenter-rds \
--db-snapshot-identifier datacenter-snapshot
aws rds wait db-snapshot-available --region us-east-1 \
--db-snapshot-identifier datacenter-snapshot
The leading waiter is not padding. create-db-snapshot refuses outright if the source instance is in any state other than available, mid-modification or still backing up included, and the waiter absorbs that instead of making you retry by hand. Note also that there are two different waiters in that block. db-instance-available watches an instance, db-snapshot-available watches a snapshot, and using the wrong one silently watches the wrong resource while you assume it is working.
Then the restore, which is a single call:
aws rds restore-db-instance-from-db-snapshot --region us-east-1 \
--db-instance-identifier datacenter-snapshot-restore \
--db-snapshot-identifier datacenter-snapshot \
--db-instance-class db.t3.micro
One override. That is the whole thing, and it is worth understanding why.
A restore is not an empty instance that happens to receive data. It clones the source's configuration. Engine and version, allocated storage, storage type, VPC and subnet group, parameter group, character set, master username and the data all come across without being asked for. The restored instance in this lab came out as MySQL 8.4.5 on 5 GiB of gp2, and none of those three were typed anywhere.
What you can change at restore time is a specific list: instance class, subnet group, availability zone, port, multi-AZ, public accessibility, storage type, tags, and auto-minor-version-upgrade. The task wanted one of those, so the command has one flag.
The master password is on neither list. It comes from the snapshot and cannot be set during the restore. Changing it is a modify-db-instance --master-user-password afterwards.
One distinction that matters more than it looks. create-db-snapshot makes a manual snapshot, and manual snapshots live until you delete them. Automated snapshots age out with the backup retention window and disappear when the instance is deleted. For a pre-upgrade safety net you want the one that outlives the thing it is protecting. The flip side is that manual snapshots keep billing for storage after the instance is long gone, and they are exactly the thing people forget to clean up.
What comes along for free
Rebase and restore both start from someone else's state and put your intent on top of it. Rebase inherits master's history and replays your commits onto it. A restore inherits the snapshot's entire configuration and takes only the overrides you name.
The failure mode is the same in both directions too. Re-specifying engine and storage on a restore either duplicates what the snapshot already carries or quietly diverges from it. Rebasing a branch other people are standing on rewrites history they already have.
So here is the Day 32 question. When you rebuild something from a known-good starting point, do you know which of its properties you inherited and which you actually chose?
Day 32 down. Sixty-eight to go.
Top comments (0)