Cue the line about how a master craftsman needs to thoroughly understand his tools, as we explore another fascinating series from the great Raymond Chen (After we first share one unrelated bonus link)
- Learn to change history with git rebase!
One of Git's core value-adds is the ability to edit history. Unlike version control systems that treat the history as a sacred record, in git we can change history to suit our needs. This gives us a lot of powerful tools and allows us to curate a good commit history in the same way we use refactoring to uphold good software design practices. These tools can be a little bit intimidating to the novice or even intermediate git user, but this guide will help to demystify the powerful git-rebase.
- Stupid git commit-tree tricks, Part 1: Building a commit manually out of a tree
I take a snapshot of what’s in our internal staging repo and push it to the public repo. All of the intermediate steps are squashed out, so that the public repo isn’t cluttered with noisy history.
- Stupid git commit-tree tricks, Part 2: Building a merge commit manually out of a tree
I want the commit to be a merge of win10-1507 and the changes specific to that branch. To do this, I use the commit-tree command, but provide multiple parents. The first parent is the previous commit for the branch, and the second parent is the incoming changes from its ancestor branch.
- Stupid git commit-tree tricks, Part 3: Building a throwaway commit in order to perform a combined cherry-pick-squash
Suppose you have a series of commits you want to cherry-pick and squash onto another branch.
The traditional way of doing this is to cherry-pick the series of commits with the -n option so that they all stack on top of each other, and then perform a big git commit when you’re done. However, this mechanism churns the local hard drive with copies of each of the intermediate commits, and if there are merge conflicts, you may end up having to resolve the conflict in the same block of code over and over again.
- Stupid git commit-tree tricks, Part 4: Changing a squash to a merge
you could hard reset the master branch back to M1 and redo the merge. But that means you have to redo all the merge conflicts, and that may have been quite an ordeal. And if that was a large merge, then even in the absence of conflicts, you still have a lot of files being churned in your working directory.
Much faster is simply to create the commit you want and reset to it.
- Stupid git commit-tree tricks, Part 5: Squashing without git rebase
Since all of the commits we want to squash are consecutive, we can do all this squashing by simply committing trees.
The point is that we were able to rewrite a branch without touching any files in the working directory.
- Stupid git commit-tree tricks, Part 6: Resetting by reusing an earlier tree
This tree-based merge is the trick I referred to some time ago in the context of forcing a patch branch to look like a nop. In that diagram, we started with a commit A and cherry-picked a patch P so we could use it to patch the master branch. Meanwhile, we also want a nop to go into the feature branch. We did it with a git revert, but you can also do it in a no-touch way by committing trees.
- Stupid git tricks: Combining two files into one while preserving line history
For best results, your rename commit should be a pure rename. Resist the tempotation to edit the file’s contents at the same time you rename it. A pure rename ensure that git’s rename detection will find the match. If you edit the file in the same commit as the rename, then whether the rename is detected as such will depend on git’s “similar files” heuristic.¹ If you need to edit the file as well as rename it, do it in two separate commits: One for the rename, another for the edit.
Wait, we didn’t use git commit-tree yet. What’s this doing in the Stupid git commit-tree tricks series?
We’ll add commit-tree to the mix next time. Today was groundwork, but this is a handy technique to keep in your bag of tricks, even if you never get around to the commit-tree part.
- Stupid git commit-tree tricks, Part 7: Combining more than two files into one while preserving line history, manual octopus merging
The problem is that octopus merges work only if there are no conflicts. We’re going to have to build our own octopus merge.
cat dairy fruits veggies | sort >foodThe git write-tree creates a tree from the index. It’s the tree that a git commit would create, but we don’t want to do a normal commit. This is the tree we want to commit, but we need to set custom parents, so we’ll ask git write-tree for the tree that would be committed, so we can build our custom commit.
git rm dairy fruits veggies
git add food