Hacker News new | ask | show | jobs
by sheept 83 days ago
doesn't it? Next to the conflict markers, it'll display HEAD, the ref name, or the short commit hash.
3 comments

I'll be honest, as a fairly skilled and experienced programmer who isn't a git expert, I know what HEAD means, but when I'm rebasing I really have no idea. It all seems to work out in the end because my collaborative work is simple and usually 2–3 people only, so I'm never rebasing against a ton of commits I lack context for (because 90% of them are my commits since I'm usually dealing with PRs to my open source projects rather than someone else's).

HEAD is "the thing we're editing now" but that's not terribly useful when rebasing since you're repeatedly editing a fake history.

Git leaks a lot of implementation details into its UX. Rebasing is meant to be equivalent to checking out the "base" branch and cherry picking commits onto it. Therefore "ours" during a rebase is the base branch.

The meaning of "ours" and "theirs" is always the same, but the "base" of the operation is reversed compared to what you might be used to during merge.

Rebasing can be confusing and hard and messy, but once I learned that rule and took the time to internalize it, I at least never got confused on this particular detail again.

> fake history

That's the thing, it's not actually fake history. Git really is doing the things it looks like it's doing during a rebase. That's why you can do all kinds of weird tricks like stopping in the middle to reset back a commit in order to make a new intervening commit. The reason you can abort at any time with (almost) no risk is because the old history is still hanging around in the database and won't be removed until GC runs, usually long after the rebase is settled.

Learning git properly is pretty much "read Git book at least 3 times".

All of it makes sense and is decently intuitive once you know how internals work.

People keep imagining git as a series of diffs while in reality it's series of the filesystem tree snapshots + a bunch of tools to manage that and reconcile changes in face of merge. And most of that can be replaced if the builtins are not up to task. And the experience is getting slowly better but it's balance between power users and newbies, and also trying to not break stuff when going forward.

Now of course that sucks if programming is not someone's day job but there is plenty of tools that present simpler workflows built on top of that.

Also git store (almost?) all its operations in the reflog. They have identifier like commits so you can reset to them and restore the original state of the working directory (mostly after an automatic rebase gone wrong).
That's the thing, they're not "like commits", they are the actual original commits. It's a history of where the HEAD ref used to be. Eventually those commits will be pruned out of the tree if/when the reflog expires because there is nothing left pointing to them. But otherwise they are normal commits.
I think what the grandfather comment meant is 'like' in the sense of 'this is an example'. Not 'like' in the sense of 'sorta / approximately'.
Yes, Git is like a Copy-on-Write filesystem.

It's interesting that once even C programmers, like Linus, become really experienced, they embrace the wisdom that functional programmers are forced to swallow anyway.

> HEAD is "the thing we're editing now" but that's not terribly useful when rebasing since you're repeatedly editing a fake history.

You got two things wrong here. Firstly, HEAD isn't 'the thing you're editing now'. HEAD is what you have already committed. If you want to edit the HEAD, you have to either amend the commit or reset and redo the commit. (To make the situation even more complex, the amended or overridden commit remains in the repo unchanged, but orphaned.)

The actual thing being edited is a 'patch' that will eventually be converted into a new commit (snapshot). If you're doing a rebase and want to see the next patch in the pipeline that you're editing now, try this:

  git rebase --show-current-patch
Secondly, rebase is not editing a fake history. Rebase is creating a new (and real) history by repeatedly cherry picking commits from the old history based on the rebase plan. HEAD is the tip commit of the new history under construction. On completion of the rebase, the branch ref of the old history is switched to the new history, where HEAD is now at. Meanwhile, the old history remains in the repo unchanged, but again orphaned.

All the orphaned commits are still visible in the HEAD's reflog. You can use it to undo the rebase if you wish.

I agree that the entire thing is confusing as hell. But I have a bunch of aliases and scripts that show you the process graphically in realtime. You can use that awareness to make the right call every time. I'm thinking about converting it into a TUI application and publishing it.

I avoid this problem by not rebasing.
Seriously! I have too many years of software development experience, but I use Visual Studio UX to handle pretty much all git operations. And always merge.

I have better things to do in my life than "internalizing" anything that doesn't matter in the grand scheme of things.

I don’t like that approach, because people who work like that commit all kind of crap to repo or cry that GIT ate their homework…

Then we have line ending conflicts, file format conflict UTF8-BOM mixes with just UTF8 it makes more work for everyone like crappy PRs. Because of people for who those are things that „don’t matter in grand scheme of things”.

I happen to know a lot about git internals, but I don't think everyone should need to.

About the line ending conflicts: set up your CI once to complain about those. And help your coworkers set up their editors right once.

If it hurts, do it more often.
Hey not every rebase has conflicts. I definitely rebase when there are no conflicts, then merge.

When there are conflicts I merge „theirs” into my branch to resolve those so I keep mental model for this side and don’t have to switch. Then rebase then open PR.

I do the following to keep my sanity when doing something like rebasing a feature branch onto latest origin/master:

* First and most important: turn on rerere.

* Second: merge and resolve all conflicts, commit.

* Third: rebase.

The second step might look redundant, but thanks to rerere git remembers the merge conflict resolution. That makes step 3 have fewer conflicts; and step 2 tends to be easier on me, because we are only reconciling the final outcomes.

(Well, I'm lying: the above is what I used to do. Nowadays I let Claude handle that, and only intervene when it gets too complicated for the bot.)

You could do all that. Or you could just merge every time. I know which I find easier.
It does now by default, since v2.12 (released 2017). Prior to that you had to set the log.decorate config. Good times.
It does