Git merge vs. rebase
25 comments · 90% upvoted · source · other discussions
25 comments · 90% upvoted · source · other discussions
Do people actually use git-pull? I feel like whenever I use that I'm actually saying "Hey, Git, go ahead and completely fuck up my local repo. I might be able to get it back to a proper state within half an hour."
But aren't you rewriting history a bit if you do this? If you are doing a rebase on your branch you are getting the latest and then applying your changes on top, this will make it look as if you made the changes from that point in time, when in reality you started the changes from a point farther back.
I think he's saying that it's ok to rewrite history when you've not pushed anything up to the remote repo.
And in many projects, it's the kinder way to do it. For someone who commits frequently, but pushes much less so (like me), it makes sense.
I feel like that is changing history I don't think that would fly with my projects
If a pair of commits gets squashed in the woods with no one to hear it, does it make a sound so long as it hasn't been pushed yet?
Well, if all your commits only exist in the local repo, it makes perfect sense to want to rearrange or clean up a series of commits.
Just today, I continued work on a feature, committed, fixed a bug, committed, continued work on a feature, committed, fixed another bug, committed. By rights, I should do an interactive rebase on all these commits, laying out the bug fixes first, and squeezing the feature commits together into one bigger commit. Of course, this kind of thing always depends on the content of the commits. Sometimes that's just not cleanly doable.
I don't like squeezing commits together. I like granular commits that each accomplish one thing. It keeps my commit messages under 50 characters, serves as a nice record of my thoughts to look back over at my leisure, and makes the occasional git bisect really easy.
I've never used git bisect - can you give us a quick usage example?
It's extremely simple, and used for finding a commit somewhere in your history where something became true. You kick it off with git bisect start. Then you mark a good and bad commit. Let's say there's a bug that's appeared, and you know how to reproduce it, but after checking a bit it seems to have been there for the last few commits. You make a wild check 100 commits back and it's not there. Mark that old, good commit as good now with git commit good <older-commit>. Now mark one of the bad ones near the latest commit (or the latest commit itself) with git commit bad <recent-commit>. Using either without a commit of some sort will mark the commit you're on, and you're free to jump around with checkout to find a good spot and plant either flag as needed.
It doesn't matter which one you do first, but the instant you've marked both it finds the commit halfway between the two and checks you out there. It also has marked the bad and good ones with tags. Now you can check to see if the bug is there. If it is, mark it with git bisect bad, and if it's not present do git bisect good. Git will move the goal posts of good and bad as you mark them, and find the new halfway point each time, narrowing down the commits around you to zero in on your target. In just a few hops you'll have bisected your way, and regardless of which is last, it tells you which commit is the first bad commit. It leaves a bad tag on that one, and good tags on each of the good commits, if more than one. Now you know the commit in which that bug appeared.
It doesn't have to be a bug. It can just help you figure out when anything happened, though it's up to you to test for whatever that case is. Gary Bernhardt gave an example in his [for-pay] screencasts of automating this in bash with a ruby function that would do the testing at each hop and mark things appropriately. In seconds his function call would locate the issue, and figuring that out can be as simple or as complicated as need be. At this point, I usually tag the bad commit, then run git bisect reset to remove all the good/bad tags and send myself back to whatever commit I was at before the whole process started. Before that you can also give git bisect log <file> to log the hops to a text file.
That sounds bloody cool, but sadly I don't think I'd ever be able to use it, being a rails developer. The amount of database jiggery pokery required at each hop would make it a much bigger job. Even still, it's bloody cool.
Now, git bisect log <file> logs all the hops to a text file - what's the point of doing this?
Now, git bisect log <file> logs all the hops to a text file - what's the point of doing this?
I have yet to find out :) I'm glad I can do it, just in case, but I have no idea why I'd want to. You can also replay git bisects. That may use the log file. I know when I've just tried git bisect replay at the end of a bisect, nothing happens. I haven't read up, but it may let you run through the hops again to prove things to yourself, or to a coworker/boss, so you have two eyes on things, to verify your findings...???
As others have said, this is done before you push your changes to the upstream repository. If anyone fetches/pulls from you, don't do this.
Think of it as refactoring your source control. Your coworkers don't care that you made a typo in one commit that had to be fixed two seconds later (as long as there is no chance that another user has checked out code containing that history).
Rebase and the history rewriting tools presuppose you are not in the habit of working collaboratively, and that is true for a great many projects, branches, and individual commits. But if you are on a team, history rewriting is a really bad habit. That means rebase, 'commit --amend', 'reset --soft', etc. If you work on a team and have not been honked by history rewriting, you will.
this is bad advice, merge and rebase are two different ways of combining code they're not interchangeable.
when you merge you have a clear understanding of when and how and more importantly what was merged, and you can go back and do it again if necessary.
rebase is different, it forces you to fix conflicts on the fly by changing the original commits which is error prone and irreversible.