11 ms·
About 99.9% of the time when people talk about rebase they talk about ‘editing’ history or ‘rewriting’ history as in the first sentence of the article. I find
by gwright 7y ago
About 99.9% of the time when people talk about rebase they talk about ‘editing’ history or ‘rewriting’ history as in the first sentence of the article.
I find that terminology terribly misleading and when I was learning git and rebase it confused the heck out of me.
No commits are harmed in the operation of `git rebase`. All the commits you had in the repo before the rebase are still in the repo. Git rebase creates a new sequence of commits and after doing its work relocates the branch name to the tip of the new sequence but you can easily access the previous commits if need be:
$ git co feature-branch
$ git rebase develop
$ git co -b before-rebase-feature-branch feature-branch@{1}
- dan00 7y ago> No commits are harmed in the operation of `git rebase`. All the commits you had in the repo before the rebase are still in the repo. The same changes are still in the repo (edit: I should have said branch here), but not the same commits, because the parents and children change and therefore the hash of the commits. It is very important to be aware that the history is changed, because the previous history can not be without issues merged with the new one, which is the main pain point and the most problems that arise from a rebase.
- Sir_Cmpwn 7y agoNo, gwright is correct, and my guide fails to capture the nuance of this detail. Each commit has a link to its parent, and represents the tip of a linked list. .git/objects is a heap of all commits (and other objects), and .git/refs contains a list commit IDs that define each head (e.g. master). git rebase will often introduce new versions of a commit to the heap and update the heads to reference new histories, but the old commits stick around and can be accessed through the reflog - with their full original history intact.
- PhaseLockk 7y agoMy understanding is that the original commits will eventually be cleaned up during garbage collection if there is nothing else pointing to them. Is that correct?
- dan00 7y agoIt is right that the previous commits are still there in the repo, but from the point of git they are garbage now and going to be removed. The point are the commits reachable now from the branch.
- Sir_Cmpwn 7y agogit-gc will save stuff in your reflogs. It works pretty hard to avoid removing objects which are referenced by anything at all.
- dan00 7y agoAh ok, the point about reflog makes sense. Thanks!
- dahart 7y agoWhat’s the matter with reachable and unreachable commits? Commits no longer needed should be unreachable and cleaned eventually, that’s a feature. Git is fantastic about keeping the unreachable commits for long enough that should I actually need them for any reason, they’re usually there. The default is 90 days. The number of times I need to dig into the reflog for any reason is very low, and always because I made a mistake. The number of times I’ve lost a commit irrevocably because it was cleaned before I needed it is 0.
- ZeroGravitas 7y ago> The number of times I need to dig into the reflog for any reason is very low, and always because I made a mistake. A good UI should allow you to recover from mistakes. Like the trashcan vs rm example everyone is using. It's good that git doesn't permanently delete stuff, it's bad that you need to be a relative expert to know that. If git branch showed rebased branches and told you they would disappear in x days then beginners might feel less fear and embrace the power of git faster.
- dahart 7y ago
- AlexTWithBeard 7y agothe old commits stick around They kinda are, but that's like saying that deleted files are not deleted, but stick around for a while. While technically true, for most practical purposes _rm <file>_ deletes the file. The fact that each and every "git 101" manual has to explain how to recover deleted commits, means something is wrong. It's like saying: "Here's the key, and in case it doesn't work there's a pry bar in the garage". This is usually a pretty good indicator that the lock is broken.
- pkamb 7y agoJust have a backup branch at the same commit as the branch you're about to rebase. It'll keep all of the pre-rebase commits on that standard branch. No need for the reflog or "trashcan" or anything weird like that.
- dahart 7y agoComparing lost commits to rm isn’t a good analogy. Git has a very good safety net when you know how to use it. The problem is knowing how to use it, not that it’s not there. It’s a legitimate point that git’s UI sucks, that’s what’s wrong, and everyone agrees. But learn how to use the reflog and you will see the light!
- pmiller2 7y agoGit has very good safety even if you don’t know how to use it, provided you commit when you want something to be saved and you don’t rm the whole repo at the first sign of trouble.
- AlexTWithBeard 7y agoI think my issue is that most people should not even know how this safety net works. But every other question about git on stack exchange seems to be "how do I recover from a failed rebase".
- dahart 7y ago
- dahart 7y ago> previous history can not be without issues merged with the new one I don’t understand what you mean. Why would you rebase a branch and then merge the rebased version with the old version of that branch? I don’t think that’s a common workflow, that is a specific case you should avoid. Typical use of rebase to clean up the unpushed portion of a personal repo before pushing doesn’t normally cause any problems. The main thing that pops up for me is trying to re-order my own dependent commits when I forget that one commit touched the same code as another. Squashing them during the rebase is the easy fix.
- Chazprime 7y agoThis really should be the top comment here. Learning about the non-destructive nature of Git really helped me overcome the unease around using some of Git's more advanced features (especially in a team environment).
- thanatos_dem 7y agoOnce I learned about the reflog I got a lot more adventurous with git. Now I’m the “git expert” in my team.
- Buttons840 7y agoYep. Specifically, git will never delete a commit, unless it is old (like 30 days old or older I think?) and is not part of a branch. I suppose there may be some arcane commands to force a deletion, but it wont happen by accident or by normal usage. Like you, I felt much more comfortable using git after learning this.
- dahart 7y ago> git will never delete a commit, unless it is old (like 30 days old or older I think?) and is not part of a branch. Yeah, completely unreachable commits have 30 days, even when you run git gc. The default reflog for a branch is even longer: 90 days! https://git-scm.com/docs/git-gc https://git-scm.com/docs/git-gc
- MichaelMoser123 7y agoHow comes that I see older commits with git log -p ? I can even see what the commit changed.
- tomcatfish 7y agoThose commits are referenced. Parent comment is talking about commits that have no references to them (ie. You make a commit then remove all traces of it, it is not gone completely until after a certain amount of time).
- 7y ago
- dmitryminkovsky 7y ago> No commits are harmed in the operation of `git rebase`. All the commits you had in the repo before the rebase are still in the repo. They are still in the repo, but if no treeish item (eg. a branch) points to them, then they'll eventually get garbage collected. Still, glad to see people are trying to elucidate git rebase. A small subset of its functionality is fundamental part of my workflow and I wouldn't know how I'd use Git without rebasing.
- Sir_Cmpwn 7y agogit-gc will also preserve objects which are referred to in reflogs - it doesn't just have to be tree-ish. From the man page: >git gc tries very hard not to delete objects that are referenced anywhere in your repository. In particular, it will keep not only objects referenced by your current set of branches and tags, but also objects referenced by the index, remote-tracking branches, refs saved by git filter-branch in refs/original/, or reflogs (which may reference commits in branches that were later amended or rewound). If you are expecting some objects to be deleted and they aren’t, check all of those locations and decide whether it makes sense in your case to remove those references.
- dmitryminkovsky 7y agoI didn’t know that! Thanks so much. I was always a bit nervous about things in my reflog being GC’d.
- Dayshine 7y agoI really don't understand why git doesn't create a tag when you rebase in case things go wrong, or more generally when doing potentially gc-able actions. Pretending the average user will know how to get things back to how they are is silly.
- mixmastamyk 7y agoYep, it was designed by and for linux kernel development, that average users are using it is an accident of history.
- luhn 7y agoI'm familiar with the reflog and use `head@{n}` on occasion, but it never occurred to me that you could use that same syntax with branches. It seems obvious in retrospect—I feel silly.
- war1025 7y agoThanks for pointing this out. I completely missed it when I read the parent comment. A neat trick.
- Sir_Cmpwn 7y agoI just added a mention right after the big scary warning about how everything you do in git is non-destructive. Thanks for the suggestion!
- rusk 7y agoI'd suggest creating the branch before the rebase: $ git co feature-branch $ git branch before-rebase-feature-branch $ git rebase develop
- gwright 7y agoWorthwhile if you anticipate something going wrong, but usually things are just fine or a `git rebase --abort` will get you back to a safe place (if you are using an interactive rebase).
- humblebee 7y agoOr git `reset --hard ORIG_HEAD` > HEAD names the commit on which you based the changes in the working tree. FETCH_HEAD records the branch which you fetched from a remote repository with your last git fetch invocation. ORIG_HEAD is created by commands that move your HEAD in a drastic way, to record the position of the HEAD before their operation, so that you can easily change the tip of the branch back to the state before you ran them. https://www.git-scm.com/docs/gitrevisions#Documentation/gitrevisions.txt-emltrefnamegtemegemmasterememheadsmasterememrefsheadsmasterem https://www.git-scm.com/docs/gitrevisions#Documentation/gitr...
- pjc50 7y agoAs the old Andre Previn sketch joke says: all the right commits, but not necessarily in the right order.
- nacho_weekend 7y agoYes, I agree. Rebase has the ability to obfuscate and rewrite commits if you so choose. Rebasing to just re-order commits is totally viable and perhaps encouraged. We did this at a previous company and it made the commit history very clean to read. However, with great power comes great responsibility in a rebase, and a junior dev can easily mess things up if you don’t educate them properly.
- anamexis 7y agoOP's point was that rebasing cannot rewrite commits. It can only make new commits and change branch pointers to them.
- gwright 7y agoNope. It doesn't 'rewrite' commits. You are using the language I was calling out as confusing. It creates new commits and moves the branch to the tip of the new sequence of commits. No existing commits are changed or deleted.
- deleted 7y ago[deleted]
- Nullabillity 7y agoIt's not 'editing' the raw history files, but you're still presenting a false history to your coworkers. To me there are essentially two kinds of rebases: - Summarizing history: squashing "implemented subfeature A.A" and "implemented subfeature A.B" into "implemented feature A" - Rewriting history: moving commits around, changing the base commit, and so on In my opinion summarizing history is acceptable, you're making a creative decision that certain information will not be useful in the review/when trying to understand the code in the future. Rewriting, on the other hand, is essentially lying. You're creating repository states that never existed, and which you have never tested. In the worst case, consider the following history: * F: (master) Merge branch 'component2' |\ | * E: (component2) Fixed component 2's integration with 1 | * D: (component2) Merge branch 'master' into component2 | |\ | |/ |/| * | C: (master) Refactored component 1's API | * B: (component2) Implemented component 2 that depends on 1 |/ * A: (master) Base Yes, it could probably be completely linearized, but that would be a horrible idea. Commit B will leave the repository in a completely nonsensical state. Sure, you could squash in E to mitigate it (since, luckily, nothing else happened in component 2 in the meantime), but then you're still stuck explaining what will likely look like a bunch of really weird design decisions compared to if you had designed against component 1's new API immediately. Historical context matters. If in doubt, don't rebase. Never `git pull --rebase` blindly.
- gwright 7y agoIt isn't lying. Advocates of rebase are always talking about a work flow where you are curating a set of proposed changes before merging into your "public" branches (.e.g, development or master). No one is advocating that you use rebase on your public branches or basically any branch that has been "published". We are talking about feature branches or spikes or branches that exist just on one developer's machine.
- Nullabillity 7y agoYou're lying about the path that you took to get to that point, and you're creating a lot of (public) nonsense commits on the way there (unless you're very careful, and/or overly squash-happy). Whether you've published the true history earlier is irrelevant to that discussion.
- sampo 7y ago> All the commits you had in the repo before the rebase are still in the repo. In your local repo yes, for next 30 days. Then git will garbage collect them. But when you force push new pointers to GitHub, the GitHub repo will lose access to the unreferenced commits right away. But of course those 30 days will give you plenty of time to go back, if you changed your mind about the rebase.
- Myrmornis 7y agoYes, came here to say the same thing. > If you've made a mistake and in so doing lost commits which you needed, then git reflog is here to save the day. Seeing that sentence in the article immediately makes me think the author has poor git practices or even understanding. reflog is useful at times, yes. But it is not what saves beginners from losing work while practicing their rebasing skills. What saves them is that they didn't rebase an important branch. They made a temp branch, pointing at the HEAD of their important branch perhaps, and they screwed up the temp branch. If people would just make this one thing clearer to beginners, it would help a lot of people learn git more easily and with less fear.