the internet is not the same as it was 20 years ago. the average person is now online, but they werent before. they dont understand where they are and need protection. there is still space on the internet, or whatever the next place will be, for the enthusiasts and other minorities. if we lose internet, something new will pop up. also, 20 years ago i didnt care so much about privacy on the internet, i just needed a cultural filter for the community im engaging with. privacy has always been a game of cat and mouse. 0 chance things stay the same for long
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.
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.
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”.
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.)
This is one of my pain points, and one time I googled and got the real answer (which is why it's such a pain point).
That answer is "It depends on the context"
> The reason the "ours" and "theirs" notions get swapped around during rebase is that rebase works by doing a series of cherry-picks, into an anonymous branch (detached HEAD mode). The target branch is the anonymous branch, and the merge-from branch is your original (pre-rebase) branch: so "--ours" means the anonymous one rebase is building while "--theirs" means "our branch being rebased".[0]
I ended up creating a personal vim plugin for merges one night because of a frustrating merge experience and never being able to remember what is what. It presents just two diff panes at top to reduce the cognitive load and a navigation list in a third split below to switch between diffs or final buffer (local/remote, base/local, base/remote and final). The list has branch names next to local/remote so you always know what is what. And most of the time the local/remote diff is what I am interested in so that’s what it shows first.
Let’s see if I get this wrong after 25 years of git:
ours means what is in my local codebase.
theirs means what is being merged into my local codebase.
I find it best to avoid merge conflicts than to try to resolve them. Strategies that keep branches short lived and frequently merging main into them helps a lot.
"Ours" and "theirs" make sense in most cases (since "ours" refers to the HEAD you're merging into).
Rebases are the sole exception (in typical use) because ours/theirs is reversed, since you're merging HEAD into the other branch. Personally, I prefer merge commits over rebases if possible; they make PRs harder for others to review by breaking the "see changes since last review" feature. Git generally works better without rebases and squash commits.
Wow, interesting to see such a diametrically opposed view. We’ve banned merge commits internally and our entire workflow is rebase driven. Generally, I find that rebases are far better at keeping Git history clean and clearly allowing you to see the diff between the base you’re merging into and the changes you’ve made.
"Clean" is not the same as "useful". You have to be really, really disciplined to not make a superficially looking "clean" history which may appear linear but which is actually total nonsense.
For example, if one is frequently doing "fix after rebase" commits, then they are doing it wrong and are making a history which is much less useful than a seemingly more complicated merge based history. Rebased histories are only clean if they also tell a true story after the rebase, but if you push "rebase fixes" onto the end of your history, then it means that prior rebased commits no longer make any sense because they e.g. use APIs that aren't actually there. Giving up and squashing everything to one commit is almost better in this case because it at least won't throw off someone who is trying to make sense of the history in the future.
I think that rebasing has won over merges mostly because the tools for navigating git histories suck SO HARD. I have used Perforce at a previous job, and their graphical tools for navigating a merge based history are excellent and were really useful for doing code archeology.
Generally our pattern is that every PR gets rebased into sensible commits. So in a way we are doing "squash commits" but the method is an interactive rebase. This keeps our history very pretty and clean, and simultaneously easy to grok and navigate.
Yes, I prefer that approach as well because it allows the person who authored the change to do all the work of deciding how to resolve conflicts up front (and allows reviewers to review that conflict resolution) instead of forcing whoever eventually does the merge to figure everything out after the fact. It also removes conflicts from the history so you never have to think about them later after the rebase/merge process is finished.
I don't know. Even when I'm working on my own private repositories across several machines, I really, really dislike regular merges. You get an ugly commit message and I can never get git log to show me the information I actually want to see.
For me, rebasing is the simplest and easiest to understand, and it allows you to squash some of your commits so that it's one commit per feature / bug-fix / logical unit of work. I'll also frequently rebase and squash commits in my work branch too, where I've temporarily committed something and then fixed a bug before it's been pushed into main, I'll just reorder and squash the relevant commits into one.
I completely agree, since doing rebase our history looks fantastic and it makes finding things, cherrypicking and generating changelogs really simple. Why not be neat, it's cost us nothing and you can make yourself a tutorial on Claude if you don't understand rebasing pretty easily.
The thing is, you'll typically switch to master to merge your own branch. This makes your own branch 'theirs', which is where the confusion comes from.
Not me. I typically merge main onto a feature branch where all the conflicts are resolved in a sane way. Then I checkout main and merge the feature branch into it with no conflicts.
As a bonus I can then also merge the feature branch into main as a squash commit, ditching the history of a feature branch for one large commit that implements the feature. There is no point in having half implemented and/or buggy commits from the feature branch clogging up my main history. Nobody should ever need to revert main to that state and if I really really need to look at that particular code commit I can still find it in the feature branch history.
This is what I do, and I was taught by an experienced Git user over a decade ago. I've been doing it ever since. All my merges into main are fast forwards.
Since it's always one person doing a merge, why isn't it "mine" instead of "ours"? There aren't five of us at my computer collaboratively merging in a PR. There is one person doing it.
"Ours" makes it sound like some branch everyone who's working on the repo already has access to, not the active branch on my machine.
I always checkout the branch I am merging something into. I was vaguely aware I could have main checked out but merge foo into bar but have never once done that.
I have met more than one person who would doggedly tolerate rebase, not even using rerere, instead of doing a simple ‘git merge --no-ff’ to one-shot it, not understanding that rebase touches every commit in the diff between main and not simply the latest change on HEAD.
Not a problem if you are a purist on linear history.
I was wondering when someone was going to point it out. I actually have only been using it since about 2009 after a brief flirtation with SVN and a horrible breakup with CVS.
I was thinking about creating a TUI application that points out what each part in the conflict indicator corresponds to. This idea is primarily meant for rebases where the HEAD and the ID of the updated commits change constantly. Think of it as a map view of the rebase process, that improves your situational awareness by presenting all the relevant information simultaneously. That could trivially work for merges too.
iirc ours is always the commit the merge is starting from. the issue is that with a merge your current commit is the merging commit while with a rebase it is reversed.
I suspect that this could be because the rebase command is implemented as a serie of merges/cherry-picks from the target branch.
Now git takes main and starts cloning (cherry-picking, as you said) commits from mybranch on top of it. From git's viewpoint it's working on top of main, so if a conflict occurs, main is "ours" and mybranch is "theirs". But from your viewpoint you're still on mybranch, and indeed are left on mybranch when the rebase is complete. (It's a different mybranch, of course; once the rebase is completed, git moves mybranch to point to the new (detached) HEAD.) Which makes "ours" and "theirs" exactly the opposite of what the user expects.
It will checkout origin/master and replay the current branch on top.
P.S. I had to check the man page as I use Magit. In the latter I tap r, then u. In magit my upstream is usually the main trunk. You can also tap e instead of u to choose the base branch.
one reason why i started enjoying programming less and less was because i felt i was spending 95% of the time on the problems you described which i felt were more or less the same over the years and werent complicated but annoying. unfortunately or fortunately, after coding for over 15 years for the past 4 months ive only been prompting and reading the outputted code. it never really feels like writing something would be faster than just prompting, so now i prompt 2-3 projects at the same time and play a game on the side to fill in the time while waiting for the prompts to finish. its nice since im still judged as if its taking the time to do it manually but if this ever becomes the norm and expectations rise it would become horribly draining. mentally managing the increased speed in adding complexity if very taxing for me. i no longer have periods where i deep dive into a problem for hours or do some nice refactoring which feels like its massaging my brain. now all i do is make big decisions
This is also my experience. I am personally really happy about it. I never cared about the typing part of programming. I got into programming for the thinking about hard problems part. I now think hard more than ever. It's hard work, but it feels much more fulfilling to me.
I miss the deep dives. I make time for them again. A month or two ago, I was working on a really complex problem where I relied way too much on AI, and that reliance kept my thinking about the problem relatively shallow, which meant that while I understood the big picture of the problem, I didn't really understand the intricacies. And the AI didn't either; I must have wasted about a week just trying to get the AI to solve it.
Eventually, I switched. I stopped using the AI in my IDE, and instead used a standalone Copilot app that I had to actually explain the problem. That forced me to understand it, and that helped me solve it. It demoted the AI to an interactive rubber duck (which is a great use for AI). That moment when I finally started to understand the real problem, that was great. That's the stuff I love about this work, and I won't let the AI take that away from me again.
Yeah but there was no lock; somebody put a box around the doorknob without anything holding it there, and somebody removed the box and opened the door.
There's nothing else to say about this. Also, your comment is nested even deeper within the same semantic squabbling, so it's odd that you think that it's a waste of time in light of more important things that you are also not talking about.
They're likely viewing the electronic documents by analogy to photocopies with blacked out sections where there is nothing to distinguish the text from the redacting marks and nothing you can project out. They don't know the structure of the file format and how information in it is encoded or rendered, or even that there is a distinction between encoding and rendering.
(A better analogy might be the original physical document with redaction marks. If the text is printed using a laser printer or a type writer, and the marker used for redaction uses some other kind of ink - let's say one that doesn't dissolve the text's ink or toner in any way - then you can in principle distinguish between the two and thus recover visibility of the text.)
File formats are complicated. The only reliable way to redact is to reduce that complication to one which humans can manage. This is even true for software that is written by humans.
Plain text and flat images are my preferred formats for things which must be redacted. Images require a slight bit of special care, as the example in the underhanded C contest highlights, but it's possible to enforce visible redaction and transcription steps that destroy hidden information.
I think that doesn’t do the scenario justice. They tried to redact and did so in a way that looks visibly redacted (in screenshots many have seen) but can be uncovered.
If you say “they failed to redact data” to a layperson looking at a visibly redacted document they’re going to be confused.
You have to log into w11 anyways as a must, no avoiding it. Sure you wouldnt call this free from purist perspective but from consumer perspective it is
I just hope they sell off Alfa Romeo who knows what to do with the brand. Who the hell wants Alfa Romeo at BMW prices? The whole history has been selling affordable sport cars. They cancelled Giulietta and don't even plan on having a car in that segment. Tonale and Junior look horrible. The brand DNA all but died
you position it vertically against something in bed and keep it close enough (half a meter) so that its practically same size as tv which is 4-5 meters away and you enjoy the pixels. i love doing this few times a week when im going to sleep or just chilling