I hate rebase on shared code I don't care how clean jt looks. Don't mess with history.
I hate rebase on shared code I don't care how clean jt looks. Don't mess with history.
Everyone else that had a copy of the repo would have had a copy of the "lost" commits.
I really cannot imagine how many things would have to go wrong for weeks of work to be lost.
- Person who destroys git history
- Person who hates destroying git history
- Person who knows how to recover "destroyed" history
- Person who knows how to truly destroy git history
… tell me more!
Stuff "leaks" so much in git, that it's really hard to lose work. The only way I could see someone losing work is if they never commit or if they never push. But even if you don't push and just rebase, you're not losing work. You would have to go out of your way to delete git history locally.
You just need to find an old commit ID somewhere, normally the reflog.
The old stuff will go away on its own eventually due to git's self-maintenance procedures removing unreachable commits, or it can be done forcefully by adjusting the gc parameters to get rid of it.
The Gitsatz Haderach
So we released at the end of our cycle. It gets used for, if a vague memory serves, end of months billing. Meanwhile someone on another team "merged" our code, and actually randomly dropped a big chunk of our work. A week later they release. Some (but not all) of our features disappear. We pull that and don't notice because we're on a new sprint. Wait until the end of the month, users try to do billing. "Hey, why did you take away those features you built for us a month ago?" "What, we never...?"
We had no clue what happened.
This got to repeat a couple of times before we figured out what must be happening. We made changes to the release process so we could track what was actually released each time, with its history. We tracked down who we thought was making the mistake, but didn't have enough evidence to prove it to his manager. That didn't stop the idiot from making the mistake, but it did streamline the process of recovering it. Meaning we had the version with our feature, we had current code, and "just" had to sort out conflicts rather than rewrite from scratch.
Now that you've heard the story, can you see how weeks of work could be lost before we figured it out? And can you understand how we could have lost history?
This was a decade ago. At my next job we had more competent people. But there we had a huge debates between rebase and merge people. There are arguments on both sides. My conclusion was that about 90% of the time, rebase makes things simpler and easier. But that remaining 10% of the time makes the 90% not worth it.
Just learn how to merge properly.
What you're describing does sound awful, but I'm pretty sure that idiot could have found a way to mess up a merge. The entire workflow sounds completely fucked, I'm not convinced it's entirely fair to blame rebase in that case.
> Just learn how to merge properly.
I know how to merge and rebase properly. My favorite PR merge strategy is rebase + merge --no-ff. So your master branch is nice and linear, but you can still see where your PR merges came in. Let's you have a "all PRs get squashed" view of the world by just adding '--first-parent' to your git commands, but also lets you have the inner details for when you're git bisecting or spelunking trying to figure out why a certain line exists.
Most people hate what I describe though, similar to mixing spaces and tabs.
I agree that the workflow was a mess in multiple ways. A lot of which were organizational decisions that I was in no position to influence.
Your favorite PR strategy is fine if you're doing it locally. However when it is done on master, you're going to have to get master again by force. Because changed history creates conflicts. Which means that you're going to have to hope that everyone only did it your way, and no idiot created conflicts in some other stupid way that you'll suffer for later.
I'd prefer to merge to head early. Merge to head often. Merge from head often. Don't have long-running shared branches. This does take some other forms of discipline though.
Master is always locked down anyway by "something" - no idea what the technical term for Github/Gitlab/Bitbucket is. Stopping people from force pushing to master prevents the sort of stuff that happened to you. Even if you don't have any "idiots", you really don't want a poor intern accidentally slightly pissing off everyone.
> I'd prefer to merge to head early. Merge to head often. Merge from head often. Don't have long-running shared branches. This does take some other forms of discipline though.
I agree with everything there, except I rebase instead of merge. So when I merge my branch to master, it's a nice neat little package that sits on top of master. It doesn't have the history of 10 merges I did while I was developing because I don't see the value in those merges.
But hey, to each their own. When I was younger, I used to get into heated debates about why I was right, now I don't really care. I'm either in a branch of my own and can do whatever I want, or working with someone and then I'll just copy whatever they do to not confuse them.
Rebase is a necessary part of a workflow even if you like merge commits. You're severely missing out if interactive rebases are not part of your toolbox.
git reset --hard origin/branch-name> git pull --rebase
git config --global pull.rebase=trueYour two feature branches would be unprotected so you can merge away if you like. When one of you wants to commit something to master, that's when you'd check for dodgy merges.
Also, "git cherry-pick" is a good alternative to merging for this use case.
No, the feature branches need to be protected or something, to enforce that they only rebase locally and don't rebase the parts that I've merged into my branch (and vice versa).
> Also, "git cherry-pick" is a good alternative to merging for this use case.
No it isn't, it means you get multiple unrelated commits for the same change, which causes conflicts and can be disastrous if a commit is deliberately reverted.
- Having a developer on your team that rebases their own feature branch
- Then tries to "git push", only for it to be rejected since a force push is required
- Then performs a "git push --force", which will force-push all of their local branches, including feature branches from other developers that they may have checked out previously
Our team uses merges because they are safe from this kind of problem, although a rebase workflow would have cleaner history. I wish that "git push --force" would not push all branches by default, and just fail unless a (remote, branch) pair or --all is given.
This is (part of) why, for most common operations, I use a Git GUI (SourceTree). Force pushing all branches can only be done by very explicitly selecting them all and initiating a force push; the default when pushing is to push only the currently active branch.
It's also overall much clearer and more intuitive to use than the Git CLI. I use it when I have to—there are things that I can't do through SourceTree, and a few things that are complicated enough that I just want to be 100% sure I know exactly what's happening—but for 99% of the Git operations I do, it handles them perfectly and without any worry that I've mistyped something or forgotten to specify a branch.
And only on working branches. I do this every single day.
To collaborate more closely and reduce (or get ahead of) conflicts. The whole point of using git at all is to be able to base your work off other people's in-progress work; if you're not interested in doing that then Subversion works better.
Assuming "H" is the hash of the current state of the repository content, consider this initial state of the repository (most recent first):
H(3) Implement feature B
H(2) Implement feature A
H(1) Initial commit
Now you implement "shiny feature", so your history in your branch looks like this: H(5) Shiny feature, improvements.
H(4) Shiny feature, initial implementation.
H(3) Implement feature B
H(2) Implement feature A
H(1) Initial commit
You tested H(4) and H(5), and everything looks good.Then you `git pull --rebase`, and your history looks like this:
H(10) Shiny feature, improvements.
H(9) Shiny feature, initial implementation.
H(8) Pulled commit C
H(7) Pulled commit B
H(6) Pulled commit A
H(3) Implement feature B
H(2) Implement feature A
H(1) Initial commit
You test H(10) because it's the current state of your repo, looks good, and merge (or create PR, whatever).With the usual pull request flows, `H(9)` (i.e. anything between your new "base" and your most recent commit) usually stays untested, entirely ignored by the developers, and you would only ever find out if you ever need to bisect.
Not usually a problem, unless you have a rule of "every commit should be verified/tested" and the untested commits have a change that doesn't prevent a build but still causes issues (e.g. something that's only visual, or a new config file was added to a "conf.d" directory and its presence changed some behavior, stuff like that).
This is exactly what backups are for.