Oh shit, git (2016)
ohshitgit.com
ohshitgit.com
When I first learned git, I thought it's pretty neat. It solves merge, branch, rewind problems. Git is one of the things in life that doesn't work like the way we think. But it turns out to be a better way.
That’s not all it has going for it either. Mercurial has a concept of commit stages to make history rewriting safer. It has a commit model that enables you to work on and manipulate branches of commits seamlessly, without needing named branches. It has not just a tree of commits but also each commit tracks its own history through rewrites. You get cool commands like absorb and evolve. It’s easier to extend than Git.
The only downside to modern Mercurial I can think of is it’s still slower than Git, by at least a bit. But it can scale incredibly far with its extensibility. For example, what Facebook did:
https://code.fb.com/core-data/scaling-mercurial-at-facebook/
So why does it never seem to get consideration? I guess it’s because of the insane proliferation of Github and Linux, which is definitely more a blessing than a curse. But it’s weird. Back in the CVS and SVN days, it didn’t seem like there was ever going to be a ‘network effect’ for source control like there is today.
I kind of wish Gitlab would implement Mercurial support. I bet it would help Mercurial gain more adoption within teams working on closed source projects. I know Bitbucket does, but to be honest that doesn’t really appeal to me much.
I think that's true if you're coming from something like Perforce or Subversion and have some notion of what the expectations are around those environments. It's much easier to translate that model into Mercurial commands.
But in the end it's deceptive; git, ugly as it is, reflects the underlying data model. Once you understand that data model, and how the basic commands manipulate that model, then it becomes a very natural language for manipulating the commit chain.
If there's a downside, it's that git doesn't let you get away for too long without learning the data model, while Mercurial lets you live in blessed ignorance until the first time it doesn't work like svn does, and then you're stuck because its data model is not as cleanly exposed, so you need to gently massage it into a good state rather than just surgically moving it to where you want it to be.
It allows git to record and re-use how merge conflicts are resolved. It's an esoteric command that 9/10 users will probably never use. But if part of your day job is, say, merging your startup's[1] local development with that of an upstream project[2], you will come to love this command very very quickly.
1) Rockmelt; 2) Chromium.
The first year or two of Git usage ended in many, many incidents of lost work. That is not good UX for source control, its the most catastrophic UX imaginable for something designed to track source changes...
That’s why the site this post is about exists. If you don’t know about reflog, you are severely disadvantaged... and many of my coworkers at my previous employer certainly did not.
I guess the real point is, just because we, who are experienced with Git, now have a good experience with Git, does that mean Git UX is good? I’d argue no. Bad UX doesn’t usually make software unusable or even unproductive necessarily, in my experience it usually means something more along the lines of, it’s unintuitive and confusing, and takes much more time to learn.
A good example would be something like Blender, especially pre-2.8. Is it bad? No! It’s good software. Is it good from a UX perspective? Well, it has unfamiliar paradigms and it tends to confuse people, so I would say no, and that’s why they’re trying to improve it.
In fairness, Git has done many small things over the years to improve its user experience and to reduce instances of data loss. But Facebook’s chistedit vs rebase -i is a pretty great example of UX differences imo. Git could catch up easily in some of these aspects.
But having said that, I really feel like the experience of expert users also matters and is often overlooked in discussions of UX. And I just don't think people love software when their experience using it is terrible.
This is, like, the very definition of bad/sloppy UX.
But, IMO, the real sea change there is the shift to a DVCS, and not Git's interface. Within the set of DVCS that I've used, I find Git to be easily the most difficult to understand and work with, and the one where I have to spend the most time reading and grokking abstruse explanations of the tool's implementation details in order to figure out how to deal with some edge case.
I don't think that I'm the only one. My sense is that people whose only DVCS is git are much more likely to love it unconditionally than people who came to git after having first spent significant time on some other DVCS. For my part, as another concrete example, I have to spend a lot more time helping junior devs with source control snafus now that I'm at a Git shop than I did when I was at a Hg shop.
In summary, "Sure it's easy to use, you just have to read this lengthy explanation of its implementation details first," isn't really a compelling argument when you know that there are other competing products that are easy to use even if you didn't spend that much time on RTFM first.
That said, I am not familiar with most of the various version control systems out there, and I could easily be missing something that's genuinely better.
I don't see how people consider mercurial "easier", "the winner" or whatever.
I think it's like languages: if you're a french speaker and you translate everything into your native tongue while learning, then of course you won't ever get past roman languages. It's just different.
`git add -p` is my absolute favorite command ever. `tig` is just awesome. The gui clients are so beautiful and take away all the so called uglyness nowadays, though I just can't find myself using anything over git add -p. Nothing in mercurials commandline ever came close to these two. But yeah tig isn't really part of git but at the same time it's been around for so long it might as well be.
> In the first and second form, copy entries from <tree-ish> to the index. In the third form, set the current branch head (HEAD) to <commit>, optionally modifying index and working tree to match.
When your documentation starts by saying (in effect) "this command does several different things", that should be a very strong sign that your command UX has some issues.
[1] https://www.mercurial-scm.org/wiki/RecordExtension?action=in...
[2] https://www.mercurial-scm.org/wiki/CrecordExtension?action=i...
There's just something about git branches and refs that makes sense to me. The CLI commands are a mess, of course—why can "reset" do everything?
Fossil is another matter. Fossil defies pithy car analogies. Integrating the bug tracker into the version control alone is a game changer, and that's not even all that Fossil does.
Sure, the model is similar and Fossil is different. But that is kind of an important note. If Fossil can’t be compared on the same level, maybe that’s a sign it solves fundamentally different problems.
In most setups, the bug tracker and source control are separate, but that doesn’t mean you can’t get bugtracking alongside code either, GitLab provides everything from bugtracking to CI to deploying stuff to Kubernetes.
Not to say Fossil isn’t cool or doesn’t have its place, but if I disagree with the philosophy (and I do, fundamentally,) then I don’t feel like I lose much using alternative software suites.
Or Fossil provides a superset of the others. Like comparing a corkscrew, which only opens bottles of wine, to a swiss army knife that has a corkscrew. They both solve the same problem, but one of them also solves other problems and is generally a more useful tool to keep around in your pocket.
The world didn't lose much with Hg losing to git. But with Fossil losing, we lost a great deal. As a consequence we have a world where people feel locked into the proprietary bug trackers their git host provides. Had Hg prevailed, that situation would be no different. The only way the world would be different if Hg had prevailed would be fewer posts on HN whining about git's interface being obtuse. Not really a substantially different reality, is it?
It's aiming to create a fossil-like distributed bugtracker on top of git, with bidirectional importing from e.g. github's and gitlab's APIs.
I mean hell, trac and redmine have been around forever now. Is an open source wiki+bugtracker that revolutionary?
My best guess is that there’s some benefit of merging the source control in, but I’m not sure; it’s not like other environments can’t provide integrated bug tracking.
And then do what with it, import it into your own bugzilla instance? Have you ever tried that? It's a nightmare.
At my previous org I imported our entire Github org into Gitlab EE. Don’t remember much pain, seemed pretty easy to me.
I want to be able to do a code review, deal with an issue, make changes to source, test etc offline/on a branch and then push all of the changes/impacts (source, issue, comments etc) for distribution to other developers.
As a car guy, Honda makes cars. Toyota makes appliances for people who hate driving.
So does the analogy still fit? And which one is better for people that are merely putting up with the necessity for source control rather than enthusiasts?
Keith Packard's "Repository Formats Matter" post nicely captures how meaningless it is to focus on this sort of thing in the long term: https://keithp.com/blogs/Repository_Formats_Matter/
I.e. yes Git has some UI issues, but those are fixable, whereas e.g. Subversion's UI was way better than Git in the early days, but its repository format limitations inherently weren't fixable.
> Mercurial has a concept of commit stages to make history rewriting safer.
Yeah that's a really neat feature. For what it's worth some people at Google seem to be working on trying to get an equivalent feature into Git.
> For example, what Facebook did[...]
Much of what drove Facebook to Mercurial has since been addressed, e.g. "status" times being slow due to lack of inotify-like integration. That's now a feature of core git. Some of the rest is being worked on and actively upstreamed, e.g. from the GVFS effort: https://vfsforgit.org
In theory, yes. However, a decade on an I'm not sure any UI issues have been fixed?
It turns out 'legacy' is a hard problem, including just for UI "porcelain". In part because people are used to what there is. (Which is a reason it's hard to get people to switch to something that isn't git either -- enough people have figured out how to do what they need with git as it is, and most people don't like having to learn new tooling).
Everyone agrees that moving from svn to git was a net positive, nobody likes svn better.
Git is pretty good.
But it's UI can be weird. And I am pretty sure if we check back in another 10 years, if people are still using git, it will be with the same basic UI model, little "fixable" will have been fixed.
Doesn't mean it'll be a disaster, we're doing okay with git. But I don't think the "UI issues are fixable" argument carries much weight here.
(Other alternatives may have been better, it can sometimes be a mystery or subject to debate why one product "wins".)
There's also cases where Git's was and still is top-notch, e.g. in the use of terminal colors by default, and opening a pager for you smartly. Both of those were cases where Mercurial trailed behind for a while, although I think now it's caught up in that area.
I concede that Git's command-line UI still sucks in a lot of areas.
What I was pointing out with the "Repository Formats" reference is that one shouldn't conflate deficiencies in the underlying formats with UI deficiencies.
The latter is easy to fix, and the git command-line client doesn't have a monopoly on fixing those things. There's plenty of other top-notch UIs for Git. E.g. Emacs's Magit, and GUI clients like GitKraken, Sublime Merge etc.
Whereas the inverse isn't true. You can't really build a client like Magit on top of CVS.
To reference a reply of mine to your sibling comment[1], it would have been like going through a lot of trouble to move from Mercurial to Git some years ago just because Mercurial's terminal UI didn't use coloring.
I found git easier to learn and use. Git has just a few basic concepts underlying how it works. Once I learned blobs, trees, commits, the index, and references (branches, tags, reflog, etc), I understood the model. It wasn't hard to grok a local vs remote repo, how history was a dag, and so forth. From there, I realized all the crazy commands, the baroque UI, these were all just ways of manipulating those objects in various ways. I could always figure out how to make git do what I wanted. It never fought me or tried to tell me I couldn't do something.
hg, by contrast, I felt was always trying to hold my hand. Its model was harder to understand. It required me to adapt to its workflow, instead of allowing me to adapt it to mine. It was generally much less flexible.
I found everything about git more Unix-philosophy like.
I understand completely if you don't want to think about your VCS and just want it to record your changes and otherwise get out of your way, but I wanted a more flexible and powerful tool.
There is some work being done on it, including a prototype: https://gitlab.com/gitlab-org/gitlab-ce/issues/31600#note_15...
It’s a really nice tool that I’ve been using for several years to push to git repositories.
I think the way forward for Mercurial would be to become a Git frontend, or "porcelain" as they call it. That way we would get the best of both worlds: the ubiquitous git format and hosting infrastructure; and the superior UI and workflow of Mercurial. I'm sure it would be a difficult undertaking, and might require difficult trade offs between compatibility, performance and usability. But I think it would be worth it. For one, Mercurial would become relevant again, and probably get a lot of new users. A lot of users would probably come back. And Git users would also benefit from the renewed competition between competing frontends.
Hi! GitLab employee here. Mercurial support has been a hot-ish topic internally. If you want to weigh in then please comment on this Issue, since Issue interaction plays a big part of how we prioritize https://gitlab.com/gitlab-org/gitlab-ce/issues/31600
(edit: I'm an idiot I should've read the full thread first since someone already linked it. gonna leave it up anyway)
Part of it is not wanted to learn multiple VCS's. Part of it is Github. Part of it is performance.
I honestly believe that if Github supported Mercurial, or if Bitbucket had "won" over Github, then Git:Mercurial would be something like 4:1.
Git won because it was a) unopinionated and b) powerful enough to support arbitrary workflows for any enterprise.
For the enterprise, being 'uniform and predictable' is way, way, way down lower on the list of important criteria in version control. (And in fact may even be a negative, due to the weird legacy workflows many enterprises have.)
What I will NEVER be able to understand is WHY the commands are so fucked-up, inconsistent and counter-intuitive. It doesn't have to be that way.
If git feels comfortable, it's only because you've crashed and burned it so many times or suffered though countless google searches to eventually remember the commands that are appropriate for your workflow.
I once felt it was weird till I dove in and looked at the underlying data model. Then It struck me and all the awkwardness evaporated and the commands actually make sense to me now.
It sure doesn't feel like that.
Instead it feels rather like a lump of code I'm dealing with now. The needed to add feature X, so they did a brief look for the easiest place to bolt it on. In the code I'm dealing with now that meant y2k stuff was placed in with the tracing code, I presume because the trace headers were used everywhere y2k was needed so they didn't have to add new modules.
In Git case "git reset" has so many features only tangentially related it looks to me the person adding the feature must have followed the same "I need X, I don't want to add or even think about how the CLI is structured so I'll just sneak it in with CLI command Z which looks vaguely similar". And thus we end up with the a similar mess to the code I'm dealing with.
In Hg's case someone evidently put a lot of thought into the overall mental model the CLI would create, and came up with something rather neat. Sadly it doesn't always reflect how it operates underneath, so it can confuse people when things go wrong. When the abstraction created by the CLI breaks, you end up having to do what all people do abstractions leak and learn two things: both the abstraction and how it really works underneath. But Hg's CLI is so well designed casual users will never have to deal with it.
In git's case the CLI bears little relationship to anything. Because nothing is where you expect nor does quite what you think it should things go wrong really quickly. That forces users to learn and think in terms of the underlying data structure which creates a lot of pain up front, but in the end you are always better off understanding things in terms of how they work underneath.
The interesting thing is git is _so_ bad the official git doco has a really good section devoted to describing what is happening underneath, which makes learning it fairly straight forward. Hg doco doesn't do that. While there are some descriptions of how it works underneath it's interwoven in with the description of the CLI, so you can't just read one smallish section and get the big picture. Worse, you can never be sure whether they are describing the model presented by CLI, or what actually happens to be bits and bytes. This makes the jump to real understanding how Hg works harder than git. It's weird how things turn out. I guess which is better depends on the situation.
In any case, please don't dignify git's CLI by claiming it conforms to someone's mental model. To paraphrase Linus, if it does, that someone must have been a demented ferret.
IDE's do try to smooth the edges of working with git, but it's never optimal, I know because I do occasionally have to drop into the git commandline with visual studio. I think IDE integrations just plaster over the mess and cut-off access to stuff you actually need to use from time to time. In other words, I don't think it's feasible, for instance, to only use git through the visual studio IDE.
Every once in a while people will talk about "porcelain" in the git commandline-- it's the notion of creating "ergonomic" command line semantics that behind the scenes are just compositions of the traditional shitty git commands. While I appreciate the pun, it still seems like it is avoiding the root problem.
Ultimately, I just can't understand: why isn't it possible to "clean up" the git CLI so that it's coherent and semantically compatible with how people use the thing? A million or so people use this tool for countless numbers of projects. How much time and effort could be saved by making it so that people can just remember commands, use them effectively, and stop having to throw away work again and again by blowing away folders and starting over with a "fresh" clone... simply because it's easier to start-over than to figure out the correct incantations when you're under time pressure?
Everyone says that and I've done the same. Quickly forgotten and left with the same inscrutable commands and looking up explanations on stack overflow.
Git has a shitty UI when you start with, and it still has a shitty UI years down the line.
The underlying model is neat and interesting, but that you have to know it to find any usability or elegance is an indictment.
We're stuck with it because of network effects. Git is absolutely not great.
Yup. According to Wikipedia, Linus said: "I'm an egotistical bastard, and I name all my projects after myself. First 'Linux', now 'git'."
I'm specifically talking about the CLI.
> It sucked at first
It still sucks. It'll always suck. The project is unlikely to fix any of the suck.
git restore --staged file # reset the index from HEAD
git restore --worktree file # reset the worktree from the index
git restore --source=HEAD --staged --worktree file # reset both the index and worktree from HEAD
Still in development [1] so if you think something can be improved, I'd love to hear it.
[1] https://github.com/git/git/blob/pu/Documentation/git-restore...
First, the default source changes depending on the target. In my opinion it would be more intuitive and simple if the default source is always HEAD. The documentation for --source in your link probably makes sense for a git developer, but not at all for a user. (Especially the part about its default value if absent).
Second, rename "--staged" to "--index".
So in summary, to update your examples, how about:
git restore --index file # reset the index from HEAD
git restore --worktree file # reset the worktree from HEAD
git restore --worktree --source=index file # reset the worktree from the index
git restore --index --worktree file # reset both the index and worktree from HEAD
git restore file # reset the worktree from HEAD
So `git restore file` should reset the worktree, if you allow an unflagged version of that command.
It still restores worktree from the index though. But if your workflow ignores the index, then index should be the same as HEAD. "restoring from index" and "restoring from HEAD" will mean the same thing.
By Git design, you will have to face the index when you have merge conflicts. I don't know how to avoid that (and now is not the time for fundamentally change how conflict resolution is done).
But yeah, the other times, I think you can just live just fine without knowing or touching the index.
> I can't test the index.
I'm not trying to convince you to use the index. But to me, the way I test the index is commit it. Then I could use "rebase -i" to revisit the old commits (i.e. the index content) and test or make more fixes if needed.
Would you mind giving the current commands to achieve the three things you just listed?
That way I learn the future restore command along with the current approach and you description. T
"git restore --worktree" is the same as "git checkout -- file".
"git restore --source..." should be the same as "git reset --hard -- file" if --hard was made to work with individual files.
Though "git restore" should make it clear (or clearer) to the user what they want to restore without resorting to more mysterious options like --hard/--soft. So if that's not obvious from my examples, I think I've failed :)
When you hoping to have git restore in the official git release?
If things go well, maybe v2.23.0, unless we find serious UX flaws and scrape the whole thing.
I've tried maybe 3 different GUIs for git and none of them really seemed to help that much. However my usage of git is pretty basic, with branches and tags being about as complex as I get.
The problem is, in the end, you'll still need to know the original git commands with all their quirks in addition to the easier commands, because that's what you'll find on stackoverflow or when asking colleagues or inside of error messages. And those command names will either clash with the names used in plain git or are different partially-overlapping concepts (`git reset` is the worst offender here). So eventually you'll need to memorize twice as many things.
This, exactly. Git was written by Linus Torvalds. You could literally replace the word "Git" in that sentence with any other software written by Linus Torvalds and it would remain just as valid. The guy writes software that has a learning curve, but does does its job elegantly. He's not writing software for the social-media-no-attention-span crowd.
And I feel it in my bones that there is a revolutionary GUI waiting to be invented. Why can't I drag a commit or set of commits from one branch to another? With safe, easy undo (reflog doesn't count) and super smooth conflict resolution? Etc etc.
And of course there is the interesting rabbit hole of semanitc / language aware diff. Line diffs suck in many ways.
It's one of the hundreds of of problems that I'd love to work on one day, but probably won't get a chance to. Sigh... :)
It is indeed being invented. It’s called Pijul: http://pijul.org/
This tool is based on strong mathematical theory of patches, instead of snapshot/commit-based. It seems simpler to reason with, but we’d have to unlearn a lot from Git.
It’s not suitable for big projects yet, but it’s already used by Pijul itself and other Rust components. And it already have its own „Github” called the Nest (because pijul is a bird). Pretty promising imho.
Is this a good thing? What practical problems does a strong mathematical theory of patches solve that git doesn’t? And what’s the difference between a commit and a patch? Aren’t git commits stored as patches?
I’m a math lover, but my gut reaction to that idea is that it sounds off-putting. I don’t mean that as a judgement or insult; I’m admitting my own assumption and bias here, jumping to unwarranted conclusion, not saying anything is wrong with pijul. But when the elevator sales pitch is “strong math”, it immediately makes me assume it’s too technical for a normal programmer and focused on academic ideals rather than getting practical work done as easily as possible.
The FAQ even says, “Pijul is trivial for whoever knows category theory.” Is that question really asked frequently? Words like that might convince me to never try it. ;)
No, they aren't, git commits are stored as snapshots. Each git commit has one tree, which is a snapshot of the state after the commit; zero or more parent commits; a pair of authors with corresponding timestamps; a commit message; and nothing more. Any patch you see in git is an illusion, made by comparing the commit's tree with the parent commit's tree.
So what's the advantage to explicit storage in patches over snapshots? Are diffs between snapshots not able to capture the same information that explicit patches have?
The math can be completely ignored by the user, it is just a way to guarantee that the tool will always match the basic intuition of version control. For instance:
- "Associativity" is a concept of algebra, but when applied to version control, it just means the following: let's Alice makes a commit A, and Bob makes two commits B and C. If Alice pulls B, and then pulls C, associativity means that she'll get the same as pulling B and C together. Pijul is associative, but Git is not!
- "Commutativity" is also a concept of algebra, but in practice in version control, it means that you don't have to think about feature branches. In Pijul, two patches that you could produce on different branches are "independent" anyway. Applying them in any order will always yield the same result, so you can push any of them, no matter the order in which you made them. This property means that "rebasing" becomes much simpler: it's just the action of applying and unapplying patches. Also, cherry-picking becomes the default, you don't even have to think about it.
- "Inverses": in Pijul, all patches have inverses. Sure, it sounds like `git revert`, except that `git revert` can sometimes screw things up very badly (if you `git revert` a merge commit, for instance).
Also, in Pijul, conflicts are the normal state, so you don't need anything like `git rerere`, and two users with the same patches (even in different orders) will always see the same conflicts. If you have a patch solving a conflict between two other patches, then the conflict is solved forever, and doesn't "come back".
As a conclusion, I'd say Git might be too technical for a normal programmer (which is why we have giant threads like this one on HN), but Pijul is the exact opposite.
It is as powerful as Git, but beginner-friendly, infinitely faster to learn, and more flexible. Since there can be no "bad merge", you don't have to think about your version control system anymore, and can focus on your work.
That said, there are still a few problems:
- it's not yet as efficient as Git for storage, but we're working on it. We just released the first full implementation a few weeks ago.
- it will never be as good as Git for detecting file moves, because Git is happy with a soup of blobs, whereas Pijul needs more structure (but OTOH that extra structure makes Pijul much better at "blame").
I want to understand first-hand by using Pijul what you mean by not needing rerere and having no such thing as a bad merge. Do you mean it’s not possible to make merge mistakes, or just that the default choices made by the version control system are never wrong? The only times I’ve ever needed rerere are when I made mistakes rebasing or resolving merge conflict, and I had to roll back and do a bunch of them again. In that case, I chose to manually undo my merge, so rerere is just saving time from having to repeat all of my decisions when only a few were wrong.
I like the idea of Pijul being better at blame. Of course the main thing that needs to happen is tooling. Git’s blame has been fine but the UI for it stinks. This is maybe the feature I miss from Perforce the most; the blame UI in P4V is superlative compared to git.
Moreover, merge is associative, which is the intuitive idea of a "good merge", in the sense that merging your patches one by one is the same as merging them all at once (this is false in Git).
In Pijul you can't "make mistakes rebasing". A repository is a set of patches (set as in maths), you can add patches or remove patches from the set, and that's about it. Two repositories with the same set of patches (possibly applied in different orders) are totally equivalent (in particular they have the same file contents).
So, rebasing in this case would be just the operation of adding some patches and removing others, and hence you can't make mistakes doing that, because there is no manual merge operation needed (of course you still need to solve your conflicts).
There have been a few blog posts after we first announced it saying "Oh, it's just category theory, so I could rediscover it independently in Haskell in just a few hours".
darcs was nice, up until you hit the exponential merge problem.
All our algorithms are in time at most log of the size of history, which is a double-exponential improvement over Darcs' worst case complexity.
It is so much better than Git CLI, which probably is too low level for daily usage.
I sometimes turn to magit when I want to navigate a file's history through git-blame, though.
(Also, whether it's good or not, rebasing in all of its forms becomes your second nature because it becomes so easy...)
Also, firing up the editor for things like editing diffs when doing git add -p or when editing operations when doing git rebase -i, is not that big of a deal if you keep your editor light. Vim, for me, loads in an instant.
But you noted the two main differences yourself: (1) you don’t have to think in terms of git hunks if you don’t want to, lines or any blocks are as easy to deal with and (2) you don’t have to think about whether it would be quick or not to fire an editor because you don’t have to fire an editor.
This, plus everything’s lightning fast due to shortcuts. I use Magit in spacemacs and rebasing is very easy. I’ve done it so many hundreds of times that I can tell it from memory - e.g., fixup-merging the last two commits to the third one: “lljjriff,,” (maybe there’s a quicker way) - that’s less than typing git rebase -i.
Not trying to preach, just sharing why magit users are so happy about it.
If you don’t like using ediff and really need to edit tons of hunks before staging, you could always commit/stash your dirty copy, revert back, edit files, check them in the way you want, possibly in multiple commits, then rebase your previously staged working copy on a new head and soft reset the head (it’s easier than it sounds) - which is the way I would do it in the terminal too if there was a lot to edit since it’s more fail-proof.
So I get this vague feeling that someone could build a better human-oriented suite of CLI tools on top of Git's internals―with the internals better mapped to people's more casual understanding of the concepts and their aims.
But that someone isn't me, yet.
Because the hardest part of conflicts is already the conflicting changes themselves and not the source control.
For example say I change one line of code in a big function. I rebase and that function has moved. Conflict!
I want a tool that says "here's what you changed, and here's the current state of the source". None of them do that though - they all just show the conflicts that git writes to disk - the code after you changed it, and the current code.
You basically get two copies of the function, one with your change and one without and you have to manually (visually) diff them to work out what you changed (or go back and look at your commit) and then reapply that change to the moved function.
It's really awkward and could definitely be better.
[merge]
# display 3 parts for each conflict,
# including the common ancestor
conflictstyle = diff3
There's also the option to use a semantic merge tool. AFAIK, there are only paying tools for this, and only a few languages are supported.Git's only sense of syntax is recognizing lines, and it's just not enough in too many cases.
Sadly in my (short) research everything I have found is either closed source or I can't afford to pay for it. It's even more saddening that git already has built-in bits for syntax related diffs.
You see original code, you changes, and the other side's changes—and where exactly they conflict.
The model of git feels very nice and intuitive once you grasp the central architecture -- an immutable append-only content-addressed file system. The changes mentioned here in particular just don't really fit the model. You can certainly argue that we need a better model, but in order to move commits between branches in anything other than a mechanical manner, you would have to deal with the fact that often in source code repositories there are semantic connections between unrelated changes -- one file has a change to update its interface, the other file changes to use the old interface, and now you have something that no general-purpose version control software can reasonable reconcile or even identify as an issue.
Arguable there's a space for merging tools that build on the git model and contain language semantic information, so it can actually highlight conflicts that are deeper than patch-based, but until then I'd prefer that the version control system not try to be too smart and let me repair things after it does its mechanical actions.
> The CLI in particular ... Line diffs suck in many ways
These are changes that I think are in line with what I see in the future of git -- because git just contains a series of snapshots of a hierarchical tree, diffs are a secondary thing that an external tool can be brought to bear on very easily -- rather than trying to merge patches to create a diff, you actually have two fully realized files, together, potentially, with their common ancestor, which is all the information necessary to create a fully semantics-aware diff between files or trees.
I would almost want git to double-down on the grammar of tree changes; commands like "checkout" and "reset" are very basic manipulations with fairly clear idioms. But since those words are also English words that carry a whole bunch of connotations, it makes it confusing when you use "reset" to do something that does not involve returning something to its original state.
Having a "worse" command line interface, with some commands like "git make-my-working-tree-look-like-this-ref" and "git point-this-ref-to-this-other-ref" would break the connection with traditional version control systems while allowing us to use the full power of the git model.
GitKraken might be for you, there is a nice drag-and-drop feature demonstration at https://www.gitkraken.com/git-client.
I don't think you can drag commits because a commit itself is a snapshot that describes changes to a branch so it is the branch itself that can be merged, rebased, pushed, etc. by drag-and-drop.
- [SemanticMerge](https://www.semanticmerge.com/)
Regarding language-aware AST-based diffs, I know of one serious full-time effort. Even for Java, it turned way too complex, so guys gave up.
It was disappointing to me that web interfaces do not offer something like this
I think that's fair to say. I think that's always fair to say.
Then like a year on I suddenly realized Ohhhhh it's ref-log, that makes a lot more sense!
Recently I discovered that it is found under "git help revisions".
It's worth scrounging through git(1) anyway; it mentions several other manpages for things like recommended workflows, the basic structure of the .git/ directory, not to mention gittutorial(7).
And it's remarkably hard to find using web searches, since most git documentation uses the term "commit" for these things, not "revision".[0] I found it after I discovered "git help -g", which lists "some concept guides" according to "git help".
Of course, n=1 and stuff, but there's a lot about git that is not obviously documented. Something like this should be linked to in multiple places, so even if you skim over one you'll catch it relatively early.
[0] The name of the man page was chosen to avoid conflict with the "git commit" command, I guess?
I think it might be nice to add a disclaimer saying that this is not advisable if you've already pushed the code. Suck it up and make a new commit–don't rewrite public Git history.
Or, someone might have created another branch off your feature branch, because they depend on your work. Now you've creaed a time bomb for them when they try to merge their work after you've merged your alternative-history version of it. (and the failure mode is just weird, it takes experience to identify that all those seemingly nonsensical merge conflicts are result of this situation)
Etc. It just breaks a lot of things. The Git model and bad UI are already taxing enough to work with in your head, concurrently with your actual programming and domain cognitive load, that adding the uncertainty and multiplied complexity from having history rewritten around you is just a bad tradeoff.
(This may be different if the scenario is not a team, of course...)
Do you frequently go messing with a branch assigned to single developers on your team without any sort of prior heads-up? And then surprise them with new commits next time they push/pull? I guess if you do that then none of you can ever force-push, and it's great if that works for your team, but I feel like generally people try not to interject and instead give a heads-up before messing with others' branches, after which the original dev knows others are involved and can then avoid force-pushing (or sync up when needed).
Your proposed "heads up" way can work in theory, but it introduces too much friction and risk of error, and still leaves the situation in shambles when they have a rebased branch on their laptop that they haven't pushed yet. So you need to have a multi phase protocol that requires many interactions and even then the other guy may well forget about it and do a rebase & force push out of habit in the end.
[1] Well, "branch assigned to a single developer" isn't a thing, but most feature branches are finished by 1-2 people.
But then you already know someone is reviewing that branch! So both of you avoid force-pushing.
> or when casually working together on the same thing in
Again, you both know multiple people are involved here... "casually working together" is the heads-up! You don't need another one.
> or when basing branches on other people's unfinalized work
Without any sort of hint to the guy working on that branch? How do you know it's even in a stable state to build on if you have no communication?
> or they do a force push out of habit
Yes it breaks if you screw up, but I mean then you just deal with it right? It's a dumb mistake just like any other silly mistake that can happen during committing, it's infrequent, it's on an ephemeral branch, and it's completely reversible. I don't see why someone occasionally mistakenly pushing the wrong thing (forced or otherwise) on a branch that isn't even going to exist for much longer is such a catastrophic event that you have to formulate your whole team's entire development process around avoiding that event 100% at all costs?
> Your proposed "heads up" way can work in theory, but it introduces too much friction and risk of error, and still leaves the situation in shambles when they have a rebased branch on their laptop that they haven't pushed yet. So you need to have a multi phase protocol that requires many interactions and even then the other guy may well forget about it and do a rebase & force push out of habit in the end.
Again, you don't need an explicit heads-up when you already know multiple people involved, and you can just deal with the occasional errors, as I mentioned. See above.
Well, one of force-pushing or casually working together had better be a rare exception, otherwise you'll do both at the same time. I'd rather not make force-pushing part of my normal workflow, because casually working together has more value to me.
> Without any sort of hint to the guy working on that branch? How do you know it's even in a stable state to build on if you have no communication?
Anything committed / pushed is assumed working (we tend to compile before commit). If they need to rework their changes, they'll rework their changes, but the current state of their branch is almost certainly closer to the final state of their branch than the current state of master is.
> Yes it breaks if you screw up, but I mean then you just deal with it right? It's a dumb mistake just like any other silly mistake that can happen during committing, it's infrequent, it's on an ephemeral branch, and it's completely reversible. I don't see why someone occasionally mistakenly pushing the wrong thing (forced or otherwise) on a branch that isn't even going to exist for much longer is such a catastrophic event that you have to formulate your whole team's entire development process around avoiding that event 100% at all costs?
The trouble is it's viral. By the time you've figured out it's happened, other people have probably pulled from that branch; at best they've spent time merging in the force-pushed version. More likely they've done that and then built more work on top of it, so either both versions make it into mainline and everyone is resolving conflicts, or you have to ask that person to rebase their work and create another chance for the same problem to occur.
First, I don't understand how you're suggesting this situation arises. You're talking about a bunch of people all pulling from a branch while the only guy working on it is merrily force-pushing code and has absolutely no clue that anyone else is building work on top of it. Ignoring the questionable assumption that any commit on any branch is a sane one to build on top of -- why/how in the world is there zero communication on your team when you guys start building on each others' work?
Second... how is this any different from when the dev just gentle-pushes new commits instead of force-pushing?! You all will still end up having to resolve conflicts just the same when you rebase on top of their branch again... it sounds to me like what you're against is actually rebasing, only disguised as force-pushing.
Code can be communication too. It's really nice to be able to just pull someone's branch (that they maybe mentioned in standup) without having to interrupt them to tell them you're doing it.
> Second... how is this any different from when the dev just gentle-pushes new commits instead of force-pushing?! You all will still end up having to resolve conflicts just the same when you rebase on top of their branch again... it sounds to me like what you're against is actually rebasing, only disguised as force-pushing.
When a dev gentle-pushes new commits and people merge them into their branches, it doesn't cause problems if they resolve conflicts differently, and other people who pull from their branches in turn don't have to keep re-resolving the same conflicts.
Rebase and force push are two sides of the same coin, let's not argue semantics. I say "no force pushing" rather than "no rebasing" because local rebase before you push doesn't cause problems.
But then if they communicated to you (during stand-up of whatever) their branch is ready to be used to build on, then they've made that code public and would avoid force-pushing, which is what I was saying? Conversely if they haven't told you then you wouldn't just randomly pick a commit and start building on it?
> When a dev gentle-pushes new commits and people merge them into their branches, it doesn't cause problems if they resolve conflicts differently, and other people who pull from their branches in turn don't have to keep re-resolving the same conflicts. > Rebase and force push are two sides of the same coin, let's not argue semantics. I say "no force pushing" rather than "no rebasing" because local rebase before you push doesn't cause problems.
AH so you're fundamentally against ever e.g. having a pull --rebase workflow on any branch you've ever pushed to any remote... regardless of who's working on what, what communication has occurred, whether that branch will even exist 2 hours from now, or whatever. Merge is the only valid approach in your mind, period. Honestly I have zero hope of trying to convince you merge isn't a size-fits-all dogma, so I won't try anymore except to point out that git supports both workflows for a reason, and its own documentation is clear that neither is strictly superior to the other: https://git-scm.com/book/en/v2/Git-Branching-Rebasing#_rebas...
Every morning everyone mentions what they're working on (and so implicitly what their current branches are about). Most of the time there's no particular reason to pick their branch up, but sometimes there is. The only way that can work is if you never force-push the branches you're working on.
> Conversely if they haven't told you then you wouldn't just randomly pick a commit and start building on it?
Anything that's pushed to the repo is presumed good to pick up, that's what the repo is for. If it's not ready for others to pick up then why push it to the repo at all?
> AH so you're fundamentally against ever e.g. having a pull --rebase workflow on any branch you've ever pushed to any remote... regardless of who's working on what, what communication has occurred, whether that branch will even exist 2 hours from now, or whatever. Merge is the only valid approach in your mind, period. Honestly I have zero hope of trying to convince you merge isn't a size-fits-all dogma, so I won't try anymore except to point out that git supports both workflows for a reason, and its own documentation is clear that neither is strictly superior to the other: https://git-scm.com/book/en/v2/Git-Branching-Rebasing#_rebas....
Did you read your own link? The last paragraph says exactly what I've been saying.
You push... because you have 2+ repos you want to sync? Because you don't want to lose data if you mess things up locally? Because the remote repo runs tests on push that you want to run before you've finished the entire branch? Because someone else needs to be able to pick up your code if you get hit by a bus tomorrow? Because your boss or coworkers might need to be able to look at your code in an emergency? There are a million reasons to push that don't imply every commit on every pushed branch being suitable for splitting into another branch...
> Did you read your own link? The last paragraph says exactly what I've been saying.
It says that because they're (a) assuming if you've pushed code then you've probably published, which is a safer assumption but exactly what I've been arguing is not necessarily the case (and I've been trying to explain when it's not), and more generally (b) because they're erring on the side of caution. Which makes sense; I'd give the same advice to someone who's learning git. That's is exactly why the previous paragraph says "hopefully you’ll see that it’s not that simple" and "it’s up to you to decide which one is best for your particular situation". Those sentences aren't there for decoration. And it's why there is such a thing called a rebase workflow and why they made --force-with-lease and... again, I can't hope to convince you of the value of this workflow, just like how I can't convince you to use tabs over spaces (or tabs and spaces...). What I was doing was explaining how/when it can be useful while avoiding the problems you're hoping to avoid if you haven't already dogmatically rejected the rebase workflow from the start.
They pretty much do though? If your team isn't in the habit of force-pushing then almost all commits are suitable for pulling into another branch, it's not some big deal that requires special preparation. If it's good enough to run remote tests on, it's compiling and presumably working locally. If it's good enough for someone else to pick up if you were hit by a bus, someone might want to pull from it anyway. If it's worth your boss or coworkers looking at in an emergency, it's worth them looking at in a non-emergency.
Of course pulling a co-worker's branch before it's made it into master will (generally) get you less polished code, with some risk that the remote tests will be broken, there will be code that would fail code review, etc.. That's understood and that's the price you pay for getting immediate access to their work-in-progress. It's still very worthwhile as a way to understand the direction of their changes and avoid a lot of unnecessary conflicts in the cases where you know you're working in the same area.
> What I was doing was explaining how/when it can be useful while avoiding the problems you're hoping to avoid if you haven't already dogmatically rejected the rebase workflow from the start.
You didn't explain anything! I gave a specific explanation of how rebasing will cause more conflicts than non-forcefully pushing new commits (and FWIW that comes from direct personal experience of both workflows). You went straight from arguing about terminology to accusing me of dogmatism.
To you "he pushed his commit and tests got triggered" implies "his commit is compiling and working fine locally" and therefore implies "ready to build on top of"? You never push incomplete work while intending to continue it the next day... maybe on another machine? You never run across build failures on other machines when everything builds fine locally? You never see bugs that don't repro locally? You never trigger or re-trigger tests to narrow down bugs you can't repro locally but know to exist? Every single commit you push can be assumed to build on everyone's machines, pass all tests, and provide a proper foundation to branch from? And these are all true for everyone you work with too?
> You didn't explain anything!
I explained a heck of a lot?? I explained that force-push is useful on commits that you're not intending others to build on top of; I gave at least 5 common examples of why people push code despite it not being in a proper state to build on top of; I explained that rebasing on a gently-pushed commit is exactly the same as rebasing on a force-pushed commit; I explained how your teammates can force-push if you didn't somehow insist on saving the 5 seconds it would take you to communicate with them that you're going to build on top of their branch and maybe find out if you should be doing that in the first place (both of which you often should be doing anyway); I explained that if you took the few seconds to communicate then mistaken pushes would be rare and you could just resolve them by reverting or resolving commits like you normally would; I explained how git has specifically provided multiple features and documentation to support and explain both workflows because both of them have their own benefits and neither is superior... and yet somehow I "never explained anything" and "went straight from arguing about terminology to accusing me of dogmatism"?
Seriously? You don't feel even you're slightly misrepresenting things? I almost didn't even write this comment, but now I'm definitely done with this.
Nope. I wouldn't want to switch machines halfway through a change that left the code broken. I'd either get to a point where it was working, or just not push. (Being able to break changes down into small pieces while keeping the codebase working at all times helps, but that's a good skill to develop anyway - it makes automated bisection in the future much more effective).
> You never run across build failures on other machines when everything builds fine locally? You never see bugs that don't repro locally? You never trigger or re-trigger tests to narrow down bugs you can't repro locally but know to exist? Every single commit you push can be assumed to build on everyone's machines, pass all tests, and provide a proper foundation to branch from? And these are all true for everyone you work with too?
Occasionally you'll hit a transient or machine-dependent bug, sure. (That can happen in master too, though it's less of a risk). But again, it's not a big deal when that happens. If you discover a bug in someone's branch then that is a situation that's worth interrupting them to talk about. You coordinate, they fix it and you pull their branch again - or you fix it and they pull your branch. Most likely you need to communicate and collaborate on the fix in any case, since you know what the bug is while they know what their changes were.
There's a cost and a risk associated with working with code that hasn't yet passed all of the master gates. But there's also a cost and a risk to working on a particular code area without seeing your coworker's changes in that area, even if they haven't gone into master yet. Most of the time the tradeoff works in favour of pulling their branch, IME.
(And even arriving at the "ok i could fix this with rebase" diagnosis will have been painful and frustrating and eaten time & energy, and you can't be sure you got away with it before actually doing it and waiting if your teammates will come kick you in the nuts. or worse, silently spend a day untangling their work.).
It's just fundamentally unsound.
Huh? You don't rebase their branch. You rebase your own changes on top of their branch, which they happened to recently rewrite. Just like you might rebase your changes on top of master after master has undergone changes. I think the parent's point was that it doesn't matter if the branch was rewritten or just extended; either way you rebase the same commits on it the same way. (If you're one of those people who's against the notion of rebasing entirely then that's a separate debate we can have another time, but you need to separate that from the force-push issue.)
There are definitely exceptions, and a co-worker that I work closely with have one just last week. My stuff is based off his. If he changes anything (adds new commits, or rewrites current ones), my "pull" is just to rebase onto whatever his new tip is. If I have a fix to his commit, I just commit that in my branch; at that point it isn't work the trouble. When we're done, we either merge his (and I rebase onto master) or we just feel lazy and merge both by merging my branch.
Having a branch on a branch on a branch has yet to happen, and honestly, at that point, I think we'd just wait for the upstream branch to land.
Honestly, for 99% of cases, you know what the effects of rebasing/rewriting will be. It's feature branch that only you're touching? Go wild! In the middle of a code-review? Maybe write the commits in the --auto-squash format, and wait until approval before actually running rebase. Someone else is based off it? Maybe give them a heads up. For the few spots where it's ambiguous, a little bit of "hey, I'd like to X for reasons Y, ok?" is all it takes.
I push my code up to my origin as soon as I can and as I go I’ll fix up my commits and force push.
There are some advantages for me anyway. Pushing to origin kicks off some smoke tests and end to end tests that are fairy slow and cumbersome to run on my dev machine. That helps me catch bugs earlier, especially since I’m working on a Microservices architecture. Also it acts as a backup for if my dev machine dies on me. I prefer to fix up and force push to create a clean logical story from my code rather than leave in spurious commits which exist only to fix linting for example.
So it would change your advice to: Don’t rewrite public Git history unless you can assume it’s read only.
Patches for Linux get rewritten all the time till they are finally merged.
I see about 600 pull requests on github[1]. My understanding was that Linus moved away from mailing lists some time ago. But then I'm not involved in kernel hacking.
You're most likely making things worse. Not only will you have the mess-up commit(s), but also the un-mess-up revert-commit. It gets super hairy when reverting merges (I assume you're not rebasing + fast-forwarding if you lobby against rewriting).
There is no big deal in rebasing to a rewritten master branch. It's just like any other rebase.
IMHO, rebasing signals that someone wants to apply a patch, and knows exactly where. Merging/reverting/etc. signals someone wants to "upload his latest stuff", like to a dropbox.
In the mean time, 'thanks' to Git, the source change history became a maintenance line-item. The expectations of a clean history were raised almost to the level of expectations for bug-free code.
I can see a utility of clean feature history, but asking developers to craft the history is shifting their focus away from the actual code. As long as the source state has been saved, the source control has done its main job.
So for the most of the listed 'shits', the developer should just be able to revert, cherry-pick, and re-commit, and keep going. Nothing esoteric and hard to remember, also fairly common commands across different VCS tools. Shit happens and will happen again, no biggie, no need to blame and shame, source annotation will show the right change/comment anyway.
Like no good program, ever, to use git you have to understand all the compromises and all the internals of its data structures. What a joke.
If you need to reset with reflog a lot you're probably using git wrong.
Sure it can be useful but I don't see why it should be in a workflow.
The way it worked well was that the "--staged" flag would be implied if you had already staged some files to be committed.
But on this day I noticed that nothing bad happened from that behavior. So I time traveled back and whispered to the git devs that the interface should be made more pedantic to keep users from relying too much on git to do the right thing for them.
Now it's great because users suffer and I have plausible deniability from this now being on par with the rest of git's interface.
Seems fairly magical compared to other stuff here. To me at least. Can anyone briefly explain what it does?
git reset means 'point the current branch to this commit instead of wherver it's pointing now' (branches in git are just pointers to commits)
git reset --hard means 'also reset the state of the checkout and staging area to be in sync with the commit'
Thus, the entire spell means 'reset the current branch to make it point the the seecond-to-last-commit, also ensure my current checkout and staging area are in sync to that', or, in other words, fully forget and drop the latest commit.
This seems like mistake or a non-standard usage of the english phrase "second to last". Given a git log of:
$ git log --oneline
15e0437 - (HEAD -> master) this is the third commit
f82d1fd - this is the second commit
9180c17 - initial commit
I would call "f82d1fd" the "next to last commit" and it can be referred to as HEAD~
I would call "9180c17" the "second to last commit" and it can be referred to as HEAD~2Learn something new every day.
https://books.google.com/ngrams/graph?content=second+last%2C...
And here is one with an American corpus:
https://books.google.com/ngrams/graph?content=second+last%2C...
Ideally your code review system should be the only one to have merge rights to master. Then nobody can singularly break master at least.
Oh shit someone has fixed an old commit message that was already pushed to other repositories.
Oh shit, this commit ough to be a merge commit. The tree is good, but not the parents.
This is a horrible article. You should not bookmark it or use it. If you're not a programmer, you shouldn't use git. If you are a programmer, do yourself a favour and spend a day going through something like this: https://wyag.thb.lt/
It will make you better at git and better at programming. Git is a powerful tool and you need to learn how to use it. Imagine if people read articles like this one instead of learning how to drive.
It's probably helpful to know some basics, but do I need to know intimate details of my filesystem to use cp, mv, shell redirection? No. For most basic actions it Just Works™.
The problems in git are purely user-interface based. Other distributed systems have proven you can make a dcvs with a reasonably friendly UI.
Git is not providing you with something you understand. It is providing you with a DAG and you neither understand what that is or why you need it. The DAG is not the "internals of git". This is the big mistake. It is git. Everything about git is about building that DAG.
I'm decent at got now for the work I do so I'm cool with it. It really is an awesome tool. But for some reason its just collectively taught like shit.
But git isn't just for programmers, and cheat sheets can be good for people who just want to dip their toes in.
Graph = graph, a structure composed of a set of objects (nodes or vertices) with links between them (edges).
Directed = the edges have an orientation / a direction.
Acyclic = there's no cycle, you can't come back to a node (in a directed graph you have to follow edge direction).
In Git, the commit objects are nodes, the "parent" link(s) is a directed edge, and because commit objects are immutable you can't create a commit which refers to one of its "descendants" and thus the graph is acyclic.
It is very important to understand that commits (in fact, all blobs) are immutable in git. You can only make new things. You can't modify old things. Git doesn't delete anything for a while either.
I can lose something for you in 2 seconds in git. Have fun e.g. recovering from this:
$ git init
$ mkdir -p widget && echo Introduction > widget/readme.txt
$ git add widget
$ git commit -m "Initial commit"
$ echo Conclusion > widget/readme.txt
$ git checkout widget
$ cat widget/readme.txt # No "Conclusion"??You just demonstrated losing something that was not in git... All you've demonstrated is that neglecting to put something into git is a good way of losing it.
git checkout [<tree-ish>] [--] <pathspec>...
Overwrite paths in the working tree by replacing with the contents
in the index or in the <tree-ish> (most often a commit). When a
<tree-ish> is given, the paths that match the <pathspec> are
updated both in the index and in the working tree.
As 'widget' is a path (and not a branchname as most often), local changes will be overwritten. Together with `git reset --hard` this is a bit a dangerous operation. I must say, that I don't fully understand the help text and just remember that `git checkout <path>` throws away my local uncommitted changes.See
https://github.com/git/git/blob/pu/Documentation/git-switch....
https://github.com/git/git/blob/pu/Documentation/git-restore...
Way to suggest only incompetent people lose data like this.
But yes, GUIs do prevent this to a decent extent. Unfortunately git is not a tool for which switching to a GUI before you're comfortable with the CLI is a good idea. SVN is, but not git. I say this from personal experience as a TortoiseGit user. You really gotta learn the command-line first.
Why would you sudo rm -rf / and not expect to destroy your system? Why is --preserve-root the default when that's clearly not what the command means? Like, that -rf wipes everything recursively is one of the first things you learn with rm...
Also see https://news.ycombinator.com/item?id=19907882
(P.S. also note the question wasn't even about whether this is expected behavior, it was just about whether it's possible to lose information when using git.)
Come now, that's being a bit hysterical/hyperbolic. There's plenty of other things you could do in the course of your command-line activities that are at least as destructive.
I know your example is meant to be trivial but there are plenty of ways when you're mucking about with pipes to ruin your day.
Like I say, "it is known" that git checkout is destructive, and while I can sympathise that perhaps the "-rf" is implicit I honestly can't see how you'd end up doing something like that in day-to-day activities, and if you did ... well if you'd spent any significant amount of time on it it probably should have been checked in anyway.
EDIT if you want to see some "good" examples of how you can inadvertently trash everything take a look at https://svnvsgit.com/ (warning, not as impartial as the title might suggest).
"victime-blaming", "shoot in the face" .. I feel are fairly extreme terms to describe what's happening here. The scenario you've concocted is really only one that could occur where you didn't know what you were doing and Git (or the command line) aren't really things you'd end up using if you didn't know what you're doing.
Honestly, it faded into the background of code from the beginning. I mean, I know "Forward-port local commits to the updated upstream head" means nothing to anyone not already familiar with `git rebase` but a practical mastery of the tool is very easy to achieve.
I honestly think this is a pedagogical lack. We tell everyone it's this complex thing and that they should be scared of rebase and the reflog and they believe it. Maybe if we didn't, it'd be easier.
Like I get it, we all enjoy making fun of vi but imagine every thread about vi only filled with people harping about ":wq". It gets tiring real soon.
The truth is, it's not so easy a task to get versioning right and git does, people are not humble enough.
history|grep 'git commit'|wc -l
is 20 times larger than history|grep 'git push'|wc -lNothing shameful in someone sharing that knowledge to help a newcomer become a super-user faster.
Reason I posted top-level is that it isn’t an indictment of any specific “this is hard” poster but of the general view. I don’t know when it started becoming this common belief but it’s not true.
* Bart turnstiles: the green lit-up arrow doesn’t mean you can use the turnstile with your card. Only if that arrow is present and the Clipper reader has a green light on it can you use it. Not hard, really.
* Doors that you should push but have a handle: the presence of the handle implies a pull and yet you have to push. Hard? No.
* Applications which use a floppy disk icon for save. Not hard at all.
Honestly, I think the UI is like any tool. You learn it in a day and through use it gets so familiar you don’t even think about it. But many have complained about this and UIs are harder to evaluate, so I can easily admit I’m wrong here. The difficulty, though, that’s overstated. The absolute ease with which it came to me and to nearly everyone I know puts the lie to that.
If people _did_ learn those concepts, they wouldn't find it "impossible to grasp" and then it would be more appropriate to compare one's knowledge of git to their programming knowledge.
So many things wrong with your premises...
How do you know every git user is writing code? Is git only for code now?
And in the code case... were you born knowing how to use git? Or were you forced to learn it before you wrote a single line of code? Or were you forced not to learn it until you were a pro coder? Is it difficult to fathom something landing between these extremes?
It's a tool. Any tool can be confusing if the person isn't taught how to use it. Git requires teaching so there's a lot of room for misunderstanding.
You never have to do this, because it's virtually impossible to screw up a git repo with git commands, to the point it can't be easily fixed. And I'll tell you why and how.
As you no doubt know, all commits in git are hashed. This means that you can't change a commit in any way, only ever add a new one. Neither can you insert a commit into an existing chain: you have to rewrite the whole chain with new commits.
As a consequence, if your `master` branch, for example, is pointing to commit `aa33bf`, there will be a unique chain of commits leading back to the original commit which can't be altered.
It doesn't matter if you cherry-pick, merge, rebase or dance the fandango...if you point `master` back to `aa33bf` it will look exactly the same as it did before.
You can usually find what the branch was pointing before with `git reflog`. But you were going to clone it from github again, right? So, `origin/master` is pointing to the right commit, and you can just reset it back to that (remember to first commit any random stuff in your working dir that you want to keep):
git checkout master
git reset --hard origin/master
And, obviously, you can do exactly the same with any other branches that have been messed up.There you go. Now you too are a "git hot shot" and can wow your friends with your amazing skills. ;)
Cheers!
And confusing can be and imho in this case is, something else than difficult.
I think I'd be completely unsurprised to see an intern successfully use `rerere` on a longer project of theirs.