4 ms·
Rebase everything so you get a clean timeline without meaningless "merge of bla" commit messages. Can be made mandatory by turning your repo fast-forward-only.
by mkesper 8y ago
Rebase everything so you get a clean timeline without meaningless "merge of bla" commit messages. Can be made mandatory by turning your repo fast-forward-only.
- domlebo70 8y agoIsn't that just a merge, but done one commit at a time?
- doombolt 8y agoNo it is not, because there is no mystic handwavy commit involved. You just apply a patch basically. Much less this handwavy commit exposed to uninterested parties.
- jamietanna 8y agoGP's comment was that a _rebase_ is a merge, one commit at a time, due to replaying each commit over whatever was there. Although you still fix the conflicts each time, it means you don't have many merge commits as you go along
- wodenokoto 8y agoI'm really bad at rebase, but doesn't rebasing a branch A onto branch B, remove all the history from A?
- IshKebab 8y agoNot necessarily. That only happens if you also squash your branch first. Most people do that though because they don't want potentially unbuildable commits on master. And the value of keeping around WIP commits from a feature branch is questionable.
- de_watcher 8y agoFeature branch has to be a sequence of buildable commits that explain the feature.
- aethr 8y agoRebase has a lot of options, including squashing many commits into one or more "good" commits. This is usually a good thing if you have commits that are "work in progress" commits. You can use squash/skip to avoid having commits in your history that break the project or feature, which is great if you use git bisect. The major downside of rebase is that even if you don't squash/skip it changes the hash of every commit. This is very problematic when others have ever checked out your branch locally, or made commits that haven't been pushed. It takes greater communication between the team in my experience.
- yebyen 8y ago> It takes greater communication between the team in my experience. You can also, as a substitute for greater communication, establish a protocol around branch naming and follow it. What works for us is using the word "release" and "wip" in our branch names. If a branch is a "release" branch, then it is safe to base other work on it. If a branch is a "wip" branch, then it is not safe for merging upstream. These two are not exclusive. For example, you might have a branch "dev" and a branch "release", and maybe another branch "release-dev-wip" – these all have different properties. "release-dev-wip" is a short-lived merge target for features that might not be completed yet. It is not safe to merge this upstream, unless you've checked with all of your colleagues who merged feature branches to it, and they all certified that it is no longer "Work In Progress." The key I think to make this protocol work is to distinguish between "feature branches" and "environment branches" – dev is an environment branch, and a permanent one, so it should not be rebased. Feature branches merge back to environment branches, and environment branches are deployable. You can break this rule, say if someone hotfixes master, which is upstream from dev... but it should probably be an exception to do this, and not a regular occurrence, as many people may have already based their work on "dev" and they will all need to rebase on the new (rebased) dev, in order to get a clean merge later. This is where the communication is not always optional. It might be a better choice, if the hotfix is unavoidable, but the project is large and this type of communication is logistically impossible, to merge in reverse (checkout dev, then merge master – back to dev). It might be ugly, but it's considerate. Either way, there should be a clear protocol and no ambiguity on the matter of whether you have a feature branch or an environment branch when you hold it in your hand. Feature branches represent work, environment branches probably ought to just combine the work, and maybe keep a record of how it was deployed. The branch "dev-wip" is a temporary environment branch – it might be deployed to a dev environment for example, but you should not expect it to remain permanently in the git history. At some point, perhaps it will be renamed to describe the features it contains, and then rebased and merged back to dev. If you merged your feature to it, you might expect that you will need to keep the feature branch around, so you can rebase it on "dev" or "master" later, and finally merge it back. The whole branch might not get merged upstream at once. (You can also call it "release-dev-wip" and then, the person who looks at it will know that it may contain some completed features that for some reason were not ready to merge upstream, but perhaps should not be discarded entirely. I personally like to rebase wip branches on their upstream before discarding them, just to be sure I'm not throwing away someone's work that they may have thought they merged.) Protocol is just a different form of communication that is done up-front. If you decide on a protocol and forget to explain it to your team before you implement it, you will obviously not have solved any problems. It's also important to be clear and confirm understanding, so that you can be sure nobody is imputing meanings that you didn't intend. Some teams might choose to only do prod deploys from the "release" branch, and that anything in the "master" branch must be safe to merge to release and send off to production. You could easily get yourself into trouble if you didn't understand when your team expects to work this way. Some teams might prefer to organize their releases on a "release" branch, and then use Continuous Delivery to trigger prod deploys when the release is merged to master. Other teams might prefer to use a tag for that. Mostly I think we can all agree that you should not rewrite a commit once it has been tagged, but again, this is not something that is strongly enforced by git, so it may vary from team to team. If a release that was tagged broke prod, it might actually make sense to wipe that tag from history and reroute the master branch around it. I've never seen that, but I think you're right, the most important thing is to communicate with your team so there is no ambiguity around these kinds of expectations.
- lexicality 8y agoI highly recommend being less bad at rebase - it's one of git's best features and actually pretty simple when you get the hang of it. Being able to completely rewrite a feature branch at will before merging it (or rebasing or cherry picking or whatever) to master lets you go full on XCKD[1] with your commit messages but still compress them down to 1-2 logical "add change 1" "fix bug 2" commits at the end. Thinking about commit messages mid-flow isn't worth it ;) [1] https://xkcd.com/1296/ https://xkcd.com/1296/
- nmeofthestate 8y ago>I'm really bad at rebase Yeah, I hate rebase, but the git boys (a very clever bunch) seem to like it.
- yebyen 8y agoTake a look at `git rebase --preserve-merges` There are three ways to rebase that you should learn, and I only have 5 minutes, but I'll try to sum them up quickly: rebase -i: This takes a single upstream (parent) commit as an argument and assumes you want to change something, or everything after that. That was the most important part to understand... rebase assumes you have some stable base, and that is the commit that you want to keep. (You can still squash other commits into it, or rewrite the message, but if you want to remove it altogether, you should pick the commit before it as the stable base.) Say you have a commit where you tried something, and then three commits that follow which shored that up into a working something. You may want to use rebase -i and squash those small commits, to make one good commit. (You might also use "fixup" to erase those commit messages about fixing something, because this time you're just going to deliver one commit that works. As a reviewer, I often tell my coworkers that while history is nice, I don't need to see every little thing you tried if it turned out that simply didn't work. If you want me to review your commit history, you should review it too, and feel free to clean it up first before sending it over to me!) git rebase [original-base] --onto [new-base] This is like cherry-pick for branches. This is where `--preserve-merges` really shines. Say you have a branch that you merged another branch into, and you merged that branch into your wip-release branch, and it still isn't finished, so you want to preserve all of that history when you rebase it ahead of master, where someone else has already merged their changes in before you. The tool for this is rebase --onto. Imagine you pruned the tree, leaving the node where your work started "original-base" intact, and grafted the branch containing your work that you trimmed onto "new-base". This almost has the argument semantics of a tool like "mv" -- mv [from-file] [to-file]. Then last but not least, git pull --rebase Someone has merged into your upstream (say, master) ahead of you, and you don't have any interesting merge history that you want to preserve. You just want to pretend you started your work after they finished theirs, and any empty merge commits you might have accumulated since then can be discarded, because it's really just one feature branch you're working on. (When you get good at working this way, you might actually find that you merge more often simply because it's so easy to discard the merges when they are no longer needed.) You may want to do this sooner than later, because you suspect their will be a merge conflict with their work, and you want to resolve it now, while their work and their merge is fresh in their head... so you can ask them what they were thinking, and this merge won't be looming over you for the duration of your feature branch. Depending on how you work, feature branches or wip-release branches could live for a duration of several weeks or longer. Pull rebase works like `git pull` in how it resolves merges, except it takes your commits and applies them one at a time, rewriting each commit to resolve the merge. This strategy won't generate any new merge commits. OK, one more! This one isn't a rebase, but you'll probably want it if you are using all of the above... "merge --no-ff" – once you are an expert at rebasing, you are probably going to miss those clean merge commits, because by default all of your merges are going to be fast-forwards, and there's actually some merit to the idea of having a merge commit that shows "here, a branch was ended." Basically every Github or Bitbucket will do this for you when you merge a Pull Request without choosing the "squash" option. It's handy to have in your command-line toolbox as well. Your co-workers will know that they can base their work off of yours and they can trust that you're probably not going to squash your commits behind that point again, but you can get all of that without the overhead of opening a pull request, and putting it through a formal review. This does not have the ugly semantics of a regular merge with two parents (that is the merge that everyone complains about, which you maybe cannot readily express as one "diff" against a single parent commit). As long as you use it together with rebase you can have a clear merge history while always maintaining the capability of tracing each commit's lineage to one parent with a single, clear diff. You just get a nice empty merge commit to show that "a merge happened here." There are many ways to use rebase, and not all of them remove history. Hope this helps!