Undoing temporary changes with Git
Say, I’m on ‘master’ and have a blob:
DEBUG = FALSE CACHE_SIZE = 100 code code code
Now I start debugging in a new branch…
DEBUG = TRUE # Don't forget to turn off! CACHE_SIZE = 0 # Don't forget to set back to 100!
… fix some bugs, change some code… and merge my fixes back into ‘master’. But unfortunately I have forgotten to return these “don’t forget”-s to the original value.
How can I automate the process of returning some lines back to the original value? Or at least shorten it to a single command.
Maybe, some temporary commit, or stash, or some other technique?
3 Solutions collect form web for “Undoing temporary changes with Git”
Cameron has some good ideas for shorter term debug changes. I wanted to add a common one that works even for larger or more permanent sets of local debug changes, like if you commonly make the same “don’t forget” changes every time you add a feature. I’ve heard it called a loom, quilt, stacked branches, and a pipeline. You can find plugins with those names to help maintain this kind of workflow, but there are subtle differences between them I’ve never really grasped, and the technique is not too difficult to do manually.
The basic idea is you add another branch between master and feature, let’s call it debug. You make all your “don’t forget” changes in that branch, then branch off again from debug to make feature, which contains all your changes that will go into production as normal. Then, to remove all your “don’t forget” changes in feature, do:
git rebase --onto master debug feature
That makes it look like you branched straight from master and never added the changes in the debug branch. Then you merge into master like normal. The next time you want to add a feature, you just merge master into debug and your “don’t forget” changes are automatically reapplied to the latest upstream code. Then just create a new feature branch from debug and the cycle starts again.
Obviously, you still have to remember to do the rebase before merging into master. Cameron’s hook idea can be used to prevent merges if you forget.
There’s probably no way to automate rollback of particular lines (since Git doesn’t really have any knowledge of the semantics of file contents), but there are a number of ways you can stop this happening again in future.
- Make sure you always inspect the diffs before committing.
git diff /path/to/filewill show you the changes and you can look for “Don’t forget to…”.
- You could automate this a little bit with grep:
git diff | grep "Don't forget"
- You could even have a hook that checks for a regular expression (e.g. “Don’t forget”) and forbids commits that match. This could be in your local repository or in the repository you’re pushing to.
Option 2 is probably the easiest. It still takes a bit of discipline – you need to make sure you always put “Don’t forget” (or “TODO”, or “FIXME” or whatever) in the comment and you need to run
git diff | grep, but that’s not a lot of overhead.
Option 3 will make it easier in the long term to prevent this problem, especially if you are part of a team. Of course, anyone can change the comment to “Do not forget” (or just remove the comment altogether) and bypass the check, but it’s better than nothing.
Some users eschew the Git’s index (e.g. by always using
git commit -a and only using
git add to introduce new files), but I find the index quite useful for cases like this.
The idea is to never commit your “debug” changes in the first place.
git diffto review the changes that I can stage (i.e. the diff between the index and the working tree).
For files that do not have any “debug” changes, I use
git add <pathspec> …
to stage the changes to those files.
If some files have both “debug” and intended changes, then I use
git add -p <pathspec> …
on those files to skip all the “debug” hunks.
If a hunk has both “debug” and intended changes, then I use the split and/or edit commands in
git add -pto only stage the intended changes.
Before committing, I use
git diff --cachedto carefully review the staged changes (i.e. the diff between HEAD and the index).
If a “debug” change made it into the final index, then I use
git reset -p <pathspec> …
(possibly using its split or edit commands) to discard the “debug” changes from the index.
Note: If you do testing directly from your “debug” working tree, you should be aware that you are always testing with your “debug“ changes in place. In some code bases the presence of certain “debug” changes can significantly change the behavior of the system under test. If it is important to your team that no published commit ever fails a test, then you should take the time to test exactly what you committed (without the debug changes).
You can use
git stash after every commit to stash your “debug” changes, rebuild, and test exactly what you committed. Once the testing is finished you can
git stash pop to restore your “debug” changes to your working tree.
git reset -p was first available in Git 1.6.5 (also
git checkout -p and
git stash -p).
git add -p was first available in Git 1.5.4.