Hacker News new | past | comments | ask | show | jobs | submit login
Oh shit, git (2016) (ohshitgit.com)
412 points by IvarsIndriks 9 days ago | hide | past | web | favorite | 272 comments





Reading this article, I realize that I'm old now. I still remember wrestling with cvs, svn. Merge, branch were slow and even more challenging. It was much easier to mess up and so difficult to rewind.

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.


I’m torn. Practically Mercurial feels like it should be the winner. The commands are more uniform and predictable.

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’m torn. Practically Mercurial feels like it should be the winner. The commands are more uniform and predictable.

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.


Even if you understand the data model perfectly, git still has terrible UX. reset can do 3 different things, a "remote" can mean 3 different things depending on context... I could go on all day. Just the fact that there are parodies of git man pages should drive the point home. (Disclaimer: I love git). Oh and what the hell is 'git rerere' anyway?

re)use re)recorded re)solution.

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.


That sounds a lot like the patch theory stuff from darcs and pijul.

You love git because your experience, overall, is far from terrible. It's true that there are a lot of inconsistencies to memorize, and that's a worse UX than the equivalent without those inconsistencies, but that only matters so much. We both speak English - we can cope with inconsistencies in a language.

I don't love Git because it’s got a great UX. I love Git because of what can be done once you get past the awful UX.

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 I'm arguing that UX extends beyond command names and learning curves (while definitely incorporating those things). I'm on-board with the notion that there are aspects of git's UX that are terrible. But as a user, if your experience was awful, you would not love the software.

I mean, once you’ve gotten used to a given interface over years and years of practice, it’s obviously going to be a better experience for you. I practically have Git muscle memory at this point, to the point where I frequently type Git commands in Mercurial repositories.

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.


"It takes more time to learn" is an important aspect of UX, and as I've said there are clearly ways in which the git UX can be improved.

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.


I've heard this repeated a lot but honestly find it very untrue. I understand git internals and watched half a dozen talks on hit but still have to Google how to split a commit in two or how to safely rebase vs merge when I'm dealing with too many conflicts in my rebases. How to accept their or my commits during a merge or rebase so I can skip conflict resolution, how to do X or Y. It's just bad UX. It's okay! I don't blame the tool but repeating "it's fine if you understand how it works" doesn't help, it perpetuates the idea that this bad UX is somehow good

> git, ugly as it is, reflects the underlying data model. Once you understand that data model...

This is, like, the very definition of bad/sloppy UX.


No, I strongly disagree. Exposing the datamodel can be part of an excellent user experience if your target audience are programmers, as is the case with git. Where git goes wrong is numerous commands that do numerous very different things if given different flags. That has nothing to do with the underlying datamodel being exposed; it's just sloppy planning and design. That part is terrible UX.

Perhaps, but speaking personally, that doesn't matter much to me. What matters to me is how much easier a tool makes my job, and of all the version control systems I've used, git is easily the winner.

And I don't want to say that Git isn't an improvement over many other version control systems.

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.


Fair enough. I won't say git's UI is intuitive -- it's not. But once I crested the learning curve, I do prefer it over the alternatives I'm familiar with. When I need to do something unusual, I have better luck figuring out how with git than the others.

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 dunno, I recently switched from ~4 years of using git to mercurial, and I am finding mercurial massively easier to use.

I don't get that argument at all. I came from svn, used cvs and ss and I think I even tried to use Perforce at some point.

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.


The man page for git reset states in its first paragraph:

> 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.


Mercurial had "hg record" since 2007[1] and "hg crecord" since 2008[2]. Now you would use "hg commit -i".

[1] https://www.mercurial-scm.org/wiki/RecordExtension?action=in...

[2] https://www.mercurial-scm.org/wiki/CrecordExtension?action=i...


The Emacs magit status view has an even better version of git add -p: you can hit tab to expand and collapse diffs of files and hunks in the unstaged and staged changes, selectively s (stage) or u (unstage) everything or a file or a hunk. To split a hunk, highlight the relevant part with shift+arrows then hit s or u. It’s brilliant!

I tried learning hg and arrived at http://stevelosh.com/blog/2009/08/a-guide-to-branching-in-me... which is a crazy complicated answer to what should be the most important question.

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?


It doesn't matter if you've learned git's data model or not; its use of terminology is inconsistent and confusing, and its command interface is disorganized and asymmetrical. In that way, I suppose it is very much like a "natural language", and not like the sorts of languages we are more accustomed to using when getting things done. Even if I know exactly what I want to do to the data structure git is managing, that doesn't help me find the commands I need to type to accomplish it. I have been using git for ten years now and I still have to rely on rote memorization of specific recipes in order to get anything done, which sucks because I am terrible at rote memorization.

Hg and git have near feature parity, so I don't really lament Hg's loss so much. Sure Hg's CLI is a little bit better, but beyond that it never really offered any really compelling features over git. Mercurial and git is like Honda and Toyota; maybe one is a bit nicer than the other, but they're both offering you more or less the same thing.

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.


Hg actually does go beyond Git in many areas; I did outline a few but my favorites are the improved commit model (stages, natural branching, rewrite tracking,) the commands Git doesn’t have (absorb, evolve,) and the extensibility (see Facebook extensions.)

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.


> If Fossil can’t be compared on the same level, maybe that’s a sign it solves fundamentally different problems.

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?


If you care about distributed bug tracking you might be interested in, or want to contribute to git-bug: https://github.com/MichaelMure/git-bug

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 don’t think people feel locked into proprietary bug tracking, they just choose it out of convenience. You can export data from Github or Gitlab.

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.


> "You can export data from Github or Gitlab."

And then do what with it, import it into your own bugzilla instance? Have you ever tried that? It's a nightmare.


To be fair, Bugzilla is a nightmare by default.

At my previous org I imported our entire Github org into Gitlab EE. Don’t remember much pain, seemed pretty easy to me.


From my perspective, your last sentence is the wrong way around. It's not merging the source control into bug tracking, it's merging bug tracking into distributed source control.

I used Fossil but it became very very slow when the repo size crawled near a gigabyte.

The issue that Fossil addresses from my perspective is the integration of bug/issue tracking and the VCS. The trouble with using Git{hub,lab} or other issue tracking is that the issue control isn't distributed in the same way as the source control is.

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.


> Mercurial and git is like Honda and Toyota; maybe one is a bit nicer than the other, but they're both offering you more or less the same thing.

As a car guy, Honda makes cars. Toyota makes appliances for people who hate driving.


I hate (or at least am annoyed by) dealing with source control, although I acknowledge the need for it.

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?


My personal preference is for Honda as well, but when you sit down in the drivers seat and survey what is in front of you in a Honda or a Toyota, you'll find they have more or less the same things. Perhaps Hondas being set up to be more agreeable to "car guys" is similar to the CLI of Hg not being so obtuse, but both are still nevertheless capable of more or less the same feats. They'll both see you through your commute and either is a bad choice for off-roading.

As another car guy, I agree. Never quite thought of it that way but it's true.

Driving a Toyota feels like a game/toy. Which puts me on edge, because I feel detached from what the car is actually doing. The handling feels.... too smooth.

[flagged]


Personal attacks will get you banned here. Would you mind reviewing the site guidelines and taking the spirit of this site more to heart? We've had to ask you this before.

If someone is wrong and you know more, reply with some of what you know, so we can all learn. If you don't want to, that's fine, but in that case please don't post anything.

https://news.ycombinator.com/newsguidelines.html


Wait, we're in Nerdopolis Central here, and you just called a car enthusiast a dork? What. The. Hell!!!

O tempora, o mores...


> Mercurial feels like it should be the winner. The commands are more uniform and predictable.

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


> I.e. yes Git has some UI issues, but those are fixable

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 definitely been a lot of UI fixes in the Git command-line client. I know because I wrote some small number of those patches.

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.


Your response to the first point appears to explain why Git largely replaced Subversion, but it is not clear that it applies to Mercurial.

I'm not saying it won't, just that transitory UI issues are unlikely to drive a mass exodus from one source control system to another.

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.

1. https://news.ycombinator.com/item?id=19908932


I'm an early git contributor and long time user. At the time I picked it up, I was following and using hg as well.

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.


> 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.

There is some work being done on it, including a prototype: https://gitlab.com/gitlab-org/gitlab-ce/issues/31600#note_15...


You can use Mercurial with GitHub by using the hg-git plugin, which lets you push to and pull from a Git server repository from Mercurial.

It’s a really nice tool that I’ve been using for several years to push to git repositories.


When i started using version control for my plain text notes, I initially settled on Mercurial (because of it's supposed better support for Windows), but turns out it doesn't support unicode filenames while git does. So I went with git.

https://sr.ht supports mercurial now. I haven't used it yet, but it's a nice OSS option.

I used Mercurial exclusively for a long time. I reluctantly switched to Git when it became obvious Github was the place to be if you wanted your open-source project to get any attention.

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.


> I kind of wish GitLab would implement Mercurial support

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)


Yep, mercurial really is better, but it is also noticeably slower. I used git for about a decade, and now use hg at my job. Recently, I used used git again while playing around with something over a weekend. I instantly remembered why I loved it so much. There is really something to be said for commands resolving practically before you have noticed you pressed enter. I wonder if this is inherent or accidental; I don't know the mercurial data model well enough to understand why it would be necessarily laggier.

Python vs. C makes a big difference.

> So why does it never seem to get consideration? I guess it’s because of the insane proliferation of Github and Linux

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.


> The commands are more uniform and predictable.

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.)


Git is ugly and daunting when you start with it, but as you get to know it you start to appreciate it's beauty and elegance and it will work for you. Actually I find this with many programming concepts.

Git is neither beautiful nor elegant. It survives because it does the job and a critical mass of people has miraculously brought it to prominence.

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.


The reason "the commands are so [messed]-up, inconsistent and counter-intuitive" is most likely that you expect the commands to represent your mental model of the system when in fact they represent the mental model of the system designers.

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.


> when in fact they represent the mental model of the system designers.

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.


That's the best and most plausible explanation I've heard for why the git CLI is the way it is.

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?


> till I dove in and looked at the underlying data model.

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.


Meh.

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.


Hence the name I guess ... yeah it's ugly and annoying but you're stuck with it because it's great. Kind of like Linus himself I guess ... I wonder if that's the joke.

> you're stuck with it because it's great.

We're stuck with it because of network effects. Git is absolutely not great.


I think git is great. But that might only be because all the other version control systems I've used are worse...

> I wonder if that's the joke.

Yup. According to Wikipedia, Linus said: "I'm an egotistical bastard, and I name all my projects after myself. First 'Linux', now 'git'."


Don't use the UI. Use the CLI. It sucked at first but made my experience tenfold easier down the line.

> Don't use the UI. Use the CLI.

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 aliases make it suck less :D

Even after years of Git usage, `git reset --soft -- file`, `git reset --mixed -- file`, BUT `git checkout file` instead of `git reset --hard -- file` is ugly and symptomatic of Git's lackluster UX.

This particular pattern should be fixed in a future git (hopefully). There's a new command that supports all these

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...


Two things I don't like in your examples:

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


Since this is posted on Git mailing list, the reply is here https://public-inbox.org/git/20190515103031.GA29149@ash/

imho the natural default for any of these types of commands should be to reset the worktree, never the index. In my experience, people almost always want to review a file before they stage it, even if it's coming from HEAD.

So `git restore file` should reset the worktree, if you allow an unflagged version of that command.


"git restore file" does reset worktree. Sorry I didn't mention it. If neither --worktree or --staged is present, the default target is the worktree.

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.


I can't test the index. If the index and workspace are different, the index is wrong. I would like an option to make the index go away (or else become a buildable filesystem dir), and stash whatever I don't want to commit yet.

> I would like an option to make the index go away

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.


Hi. Interesting to know thanks!

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 --staged" should be the same as "git reset -- file" (--mixed, --soft and --hard cannot be used with individual files).

"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 :)


Thanks really very helpful!

When you hoping to have git restore in the official git release?


> 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.


Has there been any efforts to add a wrapper around git's commands to make a more friendly command line UI?

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.


There have been many attempts. I liked easygit a lot (seems to be unmaintained now: https://people.gnome.org/~newren/eg/documentation/). It had really nice docu. It took me a while to memorize all the native git replacements for `eg revert`. Gitless looks like it could fit your needs (https://gitless.com/).

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.


> Git is ugly and daunting when you start with it, but as you get to know it you start to appreciate it's beauty and elegance

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.


I agree. I still have to use svn in my workplace, but I use git for my own stuff. When things go wrong, I find it easier to fix it with git than svn.

how about star team? L)

Git is pretty nice, but I'm sure there is something much better waiting to he invented. The CLI in particular could use a ton of improvements.

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... :)


> Git is pretty nice, but I'm sure there is something much better waiting to he invented.

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.


> This tool is based on strong mathematical theory of patches

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. ;)


> Aren’t git commits stored as patches?

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.


Thank you. Sheesh I feel like I should have known this about git. That would make diffing very far apart changes super fast.

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 trouble with patches is that you can't get engineers to reliably declare whenever they copy or move code and where it came from. This is part of why svn merging was such a trainwreck, undeclared copies and moves (which look like delete+insert) almost always conflicted.

I'm one of the authors. Our goal is to make something much much easier to use than Git, and we're already succeeding in doing that.

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").


Thanks for the reply and explanation. You’re convincing me to at least try it.

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.


It's indeed not possible to make merge mistakes in Pijul, in the sense that the order between lines is always preserved: if line A comes before B in one repository, A will be before B in all repositories that have these two lines.

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).


> 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. ;)

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".


"Trivial for whoever knows category theory" is about as serious as "a monad is just a monoid in the category of endofunctors, what's the problem?"

I had a sudden flashback to using darcs, which was also based upon patches instead of commits.

darcs was nice, up until you hit the exponential merge problem.


I'm one of the authors. Pijul is indeed heavily inspired by Darcs, but we solved that problem completely.

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.


I don't know about Pijul, but it claims to have solved the exponential merge problem.

https://pijul.org/faq/


I really like the promise of pijul, but it is early to say if it is better. Conventions and tooling will be a decisive factor in the ease of use. That said I do share your optimism.

Exactly the kind of program which will benefit from being wrote in rust.

If you use Emacs, give magit (https://magit.vc) a try.

It is so much better than Git CLI, which probably is too low level for daily usage.


I use the CLI for my daily work just fine, thank you.

I sometimes turn to magit when I want to navigate a file's history through git-blame, though.


CLI is fine until you acquire a habit of committing things chunk-wise and line-wise. E.g., I have these 20 files modified, but I'll quickly skim through and add/commit the lines/chunks that are done, and then continue working on the rest. CLI promotes a different kind of workflow, like "ok, I'm done, add/commit it all, and then move on".

What do you feel the UI does better than git add -p? The main concession I have is picking arbitrary files, but I feel like I've achieved 90%+ of what I want with '.cs' or 'Controller*' etc.

It's more streamlined, it's easier to scroll through staged and unstaged changes fast and flip lines/hunks/whatever between the two as you go, and you don't have to fire an external editor upon each hunk because you're already in one - emacs or vim (if using spacemacs).

(Also, whether it's good or not, rebasing in all of its forms becomes your second nature because it becomes so easy...)


If you're selecting by hunk, you don't need to fire up the editor. I also find the hunk selection in the terminal to be excellent. It's lightning fast and I can use it blind.

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.


Agreed, selecting by hunk is ok in the terminal, I do it too sometimes when sshed someplace distant.

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.


Don't you have that backwards? With `git add -p`'s `e`(dit) option, I can (and occasionally do) add just some of the changes of a single line. I can freely edit the diff that dictates what goes in the index. With magit, I believe I'm limited to just selecting whole hunks or maybe whole lines.

It’s possible via ediff if you need it; magit devs didn’t want to implement it within magit itself IIUC because it’s fragile and changes in a temporary buffer may be lost, or something like that.

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.


You're missing out on the lightning fast pull, interactive rebase, staging and commit capabilities of magit-status. The key bindings are intuitive and excellent.

They are intuitive and excellent, but I don't think they make things that much faster if at all. There's not much difference in speed between doing `C-x g s c -a c` and typing `git ci -a`.

I feel like Git's concepts got tied into a mess in its cli tools. Like the devs maybe tried to imagine what would be the proper interface for a person not deeply familiar with the innards, but failed, and e.g. `git-show` is the result.

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.


>and super smooth conflict resolution?

Because the hardest part of conflicts is already the conflicting changes themselves and not the source control.


Sure but I've never found a GUI or editor that lets me resolve conflicts in the way I want.

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.


Even without plugin in a three-ways tool, Git's conflict can provide more information.

    [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.

What you, and also I, want is a syntax-aware merge tool, i.e. one that can tell you the meaning of the change. If a function got moved to another place, or if 2 fields swapped places, I don't want it to have the same importance as na actual change in behavior. No importance, even.

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.


Have you tried a three-way-merge tool (e.g. vimdiff, meld, kdiff3, surely some emacs way of doing it)?

Three-way diff is (probably) what you want. E.g. Intellij Idea uses it by default, and likely plenty of other IDEs and editors do too.

You see original code, you changes, and the other side's changes—and where exactly they conflict.


I spent some time with `git mergetool` to help resolve conflicts. It's hideous but it's better than staring at inline conflict markers

> 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.

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.



Can this tool make the commit history look like I'd like it to?

> Why can't I drag a commit or set of commits from one branch to another?

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.


The Git support in the Jetbrains IDEs is very good. I'll often fire up IntelliJ if I'm too tired to try and figure out how to figure out what's going on.

I agree. I did find one GUI that at least lets you drag & drop branch labels (GitX) but it's a bit buggy and Mac-only. It doesn't let you rebase via drag & drop, or cherry-pick via copy & paste. Those are pretty obvious mappings and would make Git way more obvious.

I used this semantic diff tool before (with Git) and it worked pretty well:

- [SemanticMerge](https://www.semanticmerge.com/)


swarmdb has CRDT-based merge and causal branching which is "super smooth". Lets you recombine changesets rather freely, or at least supposed to. https://github.com/gritzko/ron-cxx/tree/master/db (the project is in its early stages)

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.


> Why can't I drag a commit or set of commits from one branch to another?

It was disappointing to me that web interfaces do not offer something like this


> I'm sure there is something much better waiting to he invented.

I think that's fair to say. I think that's always fair to say.


One thing that I had been looking for for a long time, but never could find, was a description of the several syntaxes you can use to refer to specific commits. Lots of git tutorials use these magic incantations, but none point you to this crucial bit of explanation.

Recently I discovered that it is found under "git help revisions".


Or just gitrevisions(7). The manpage for git(1) mentions this under the section SYMBOLIC IDENTIFIERS. Not that that's the most obvious place to look, but if you rtfm the obvious stuff, you should stumble upon these gems naturally.

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).


In git(1) it's below LOW-LEVEL COMMANDS (PLUMBING). You cannot expect a novice to read thoroughly past something like that.

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?



Am I the only one who reads "reflog" as "re-flog", that is, to be painfully whipped again?

I knew about it for at least a year thinking it was "re-flog", and just considering it mysterious why they would have called it that. (You do often use it for "re"-doing things, maybe it's got something to do with that?) Hey, it was (and is) hardly the only mystery to me in git UI.

Then like a year on I suddenly realized Ohhhhh it's ref-log, that makes a lot more sense!


.oO( the beatings continue until morale improves! )

I will now forever. Reminds me of when you read "therapist" as "the rapist".

It's always Sunny in Philadelphia ruined "philanthropist" ("full on rapist") for me, forever.

You are not alone.

> I committed and immediately realized I need to make one small change!

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.


I think changing history is fine on feature branches that only you are working on, though. IMO the benefits of keeping commit history clean outweigh the cost of having to push with "--force".

This then discourages ad hoc teamwork because you can't touch feature branches other than the ones you own. And because the results of getting it wrong are hairy, people will tend to stay away just in case. It's a chilling effect.

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...)


> This then discourages ad hoc teamwork because you can't touch feature branches other than the ones you own. And because the results of getting it wrong are hairy, people will tend to stay away just in case. It's a chilling effect.

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).


Not as often as we'd like to[1], but it happens regularly in reviews, or when casually working together on the same thing in, or when basing branches on other people's unfinalized work, etc. If a team member is used to a force push / rebase type of workflow, it invariably means that stuff will blow up becuause they forgot they had rebased, or they do a force push out of habit, etc. If you include rebase / force push in your daily workflow, you will invariably foul up with it and forget to make an exception for the teamwork.

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 it happens regularly in reviews

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.


> Again, you both know multiple people are involved here... "casually working together" is the heads-up! You don't need another one.

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.


> 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.


> 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. Why in the world is there no communication on your team?

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.


> 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.

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...


> 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

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.


> 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?

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.


> 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...

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.


> If it's good enough to run remote tests on, it's compiling and presumably working locally.

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.


> You never push incomplete work while intending to continue it the next day... maybe on another machine?

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.


you just rebase on top of their changes. No biggie here. It does not matter one bit if their branch rewrites itself underneath.

Rebasing on rebased branches means everyone has to do the same conflict resolution again and again, and you're in trouble if one person decides to do it differently. Whereas when you merge, the first person does the conflict resolution and everyone else just picks it up.

But you can't safely rebase that branch! Having rebased it, you would have now broken it for others working on it. So this is a great example how the damage spreads and taints other branches around the history rewrite.

(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.


> But you can't safely rebase that branch!

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.)


The "own" branch is also public here and maybe collaboratively worked on

The common case is that I'm working on a feature branch, and realistically, nobody else is on that branch. If it's so big that someone else has a need to start using the code, I start looking for ways to subdivide the work that don't conflict/depend (i.e., the new work can be a separate branch based off master).

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.


So then anyone building on it would rebase the same way you just did? Which was the same way they would have done so if you had just pushed a new commit?

yeah exactly, all the FUD about rebase and shared branches is baffling to me. are people regularly getting stuck on 'git pull --rebase' and slamming into merge conflicts?

Like a few other replies, I force push on my branches all the time.

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.


I find it okay to rewrite public history in short lived feature branches. It’s typically your branch after all.

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.


That's not public git history though, those are just patches on mailing lists. Even before it goes into maintainer's lists, in my experience it's extremely rare that a maintainer will force push to their own public -next branch.

EDIT: parent is right.

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.

[1] https://github.com/torvalds/linux/pulls


The Linux kernel has a bot that reminds people that kernel development happens on the mailing list: https://github.com/KernelPRBot. Since GitHub does not allow for disabling the pull requests tab, the pull requests invariably get closed.

Did you ever look at any one of these PRs?

I force-push all the time in my feature branches. It's expected. If I'm using `git commit --fixup` and `git rebase -i origin/master --autosquash` that feature branch is going to come out clean and nice in the merge commit if I force push to my feature branch.

> Suck it up and make a new commit–don't rewrite public Git history

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.


This is indeed a job for git revert, push/pr, check out a new branch then revert the revert with better messaging :)

Same idea, but much more comprehensive: https://github.com/k88hudson/git-flight-rules

I prefer this, because it has a flow-chart http://sethrobertson.github.io/GitFixUm/fixup.html


Git is nice tool, very versatile in able hands. Thanks to its promotion (plugins and github included), the pragmatic practice of source versioning gained wider adoption and lost that beg-your-IT-dept-to-set-it-up flair.

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.


Git performs like what it is : a piece of code created by debugging a blank sheet of paper. I can detect almost no philosophy and no simplifying assumptions. The thought that you need 60+ commands (the current size of my git cheat sheet, including all the bizarre argument incantations which seem customized for all 100+ possible mistakes in git) to get through the day is an abomination. I prefer perforce which requires less than 20 commands. The only reason people use git is because, Linus.

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.


> I use reflog A LOT

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.


> git diff --staged

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.


This has been immensely useful every now and then

> git reset HEAD~ --hard

Seems fairly magical compared to other stuff here. To me at least. Can anyone briefly explain what it does?


HEAD~ means 'the second to last commit in the current branch' (ie. the second to last when you `git log`).

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.


[Edit: It seems that I was mistaken but I'm leaving this up to illustrate the confusion. In my experience "second to last" isn't common and "next to last" is much more common, which I think is why I was confused because I thought they were different, but apparently they are the same. I wonder if there is a regional usage pattern to these phrases. ]

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~2

I don't know (definitely not a native English speaker), but Wikitionary seems to agree with me: https://en.wiktionary.org/wiki/second_to_last#English .

Well, I think you may be right. See my edit above. I suspect this is a regional usage pattern and I've just always been in places where "next to last" would be used and not "second to last" so I was thinking they were different, but they apparently are the same!

Learn something new every day.


Here is an ngram analysis with a British corpus

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...


Nice tool. You should expand the search range to include 2018/2019. Quite a significant change happened since then.

Many of these problems can be avoided by using a pull-request style workflow.

I don't see how prs would help solving any of those. It's common for me to use a few of these commands before I open the PR.

They stop you from pushing anything broken to master. So you only have to worry about being in a broken state locally on your machine, not about accidentally breaking master for everybody.

Ideally your code review system should be the only one to have merge rights to master. Then nobody can singularly break master at least.


Yes, there is a balance between preventing problem or fixing problem. It is useful be prevent problems, but it remains useful to be able to fix them easily.

How? I don’t see a single issue in the post that a PR would avoid. All the problems listed happen before you’re ready to push or present to others.

Oh shit, this commit message buried by new commits must be fixed before it is propagated to other repositories.

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.


Thanks for this excellent cheat shit

The last example [0] really should reference the obligatory XKCD [1]

[0] https://ohshitgit.com/#fuck-this-noise

[1] https://xkcd.com/1597/


Git is not hard. It's very simple. But people learn it the wrong way. You have to learn it from the DAG up. If you cannot grasp how the DAG works you'll forever be reading and writing articles like this one which do not help you to learn.

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.


Don't gatekeep. Git isn't just for programmers.. it's for people that are learning.. people using it in non programming capacities and tons more. Telling people to "git gud" is not helpful. Not everyone knows, or indeed cares to know what a Directed Acyclic Graph is, and sites like this help people's anxiety who are just learning, or who have already screwed up and just want a solution.

[flagged]


Nope.

You can use Git for versioning all kinds of assets...3D models, fonts, textures, music. I know someone who stored his book on Github and took pull requests from editors.


Yes but those are hacks, no one should immediately reach for git as a tool to version EVERYTHING just because you can. If you treat git as a convenient hammer for your screw, don't be surprised when the screw breaks at an inopportune time

Are you imagining every use of git is either (1) a Computer Scientist writing Code, or (2) a hack? You can't imagine anything in the spectrum in between? LaTeX papers by academics in various fields, scientific coders (MATLAB etc.), people writing stuff in Markdown, students who are still learning even CS, etc. are all doing the wrong thing by using git?

First of all I read it as using git in those ways are hacks (in the computer sense), not that the people using git in those ways are hacks (in the bad at your job sense)

Secondly I think his point is (and I kind of agree) that while you certainly can use git to version you documents, 3D-models, and InDesign layouts, it's not necessarily the best tool for the job. Sure if you're already well versed in git go ahead and use it and if you're collaborating with people using git you're probably going to have to learn it, but at least realize that using git for not-code is probably not the best tool for the job especially if you're at the same time trying to learn git from zero.


> First of all I read it as using git in those ways are hacks (in the computer sense), not that the people using git in those ways are hacks (in the bad at your job sense)

Yes that's why I said "every use of git [...] (2) is a hack".

> Secondly I think his point is (and I kind of agree) that while you certainly can use git to version you documents, 3D-models, and InDesign layouts, it's not necessarily the best tool for the job. Sure if you're already well versed in git go ahead and use it and if you're collaborating with people using git you're probably going to have to learn it, but at least realize that using git for not-code is probably not the best tool for the job especially if you're at the same time trying to learn git from zero.

What do you see as the best tool for the job?


SVN is often better suited for such files because

a) they can grow quite large and the diffs do not compress well so downloading the entire history is quite expensive. SVN supports only downloading part of the tree and history which is useful

b) SVN supports file locking which can help prevent conflicts between editing the same file which is important because of the next point:

c) These files generally do not have diff and merge tools so branching is generally not useful and so are most of git's advantages over SVN.

That said, I am now generally using git for such files because a) I use git for code anyway and b) gitlab (especially which CI is still useful).


SVN? They should force themselves to need access to a server (or learn to run one) even if they're working by themselves locally?

What do you see as the best tool for the job?

Depends on the job. If you're making a game in Unreal, perhaps take a look at Perforce and Perforce integration that Unreal offers. Doing post work on a movie, consider something like Alien Brain. Doing some collaborative writing with a bunch of non-technical co-authors, then the tools that come with Google docs might be the best fit for you.


How about the examples I gave here: https://news.ycombinator.com/item?id=19907595

And if you're going to claim they should be using online-only tools, please explain why it's wrong for them to instead choose the tools that work locally...


so the people who maintain our site content using markdown and hugo, commit/push git, and trigger a CI build and deploy automatically MUST be developers?

answer: they are not, but they can handle basic git just fine. we aren’t some special class of super human: git is a tool, and you absolutely don’t need to know what a DAG is to use it


I know, I know, don't reply to the trolls, but at least this troll made me remember some things I'd seen.

Git for writers: https://medium.com/@sayhellotovanessa/git-for-writers-write-...

> There are better tools for other people.

Such as?


I agree that the DAG and Git storage model are at least not particularly complicated. The problem is that the Git user interface (the CLI, plus various concepts that are used in other interfaces as well, like refspecs, that are not fundamental to Git) is not very simple, and the correspondence between the DAG and mutations you may wish to perform, and Git commands, is often fairly obtuse and opaque.

I care about the internals of git about as much as I care about the internals of my filesystem.

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.


That's because a file system is something you already understand, even if you've never actually used an old fashioned paper filing system. The software is providing you with something you understand.

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.


Git isn't hard but it's usually taught like absolute shit. From the get go you're told to use 4 commands, 2 of which are usually not explained and when they are it's often hand wavey. From there on its usually people pontificating about high level philosophy while failing to give concrete working examples. At least that was my experience.

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.


It's just one of those tools that is very foreign to new users, but once you know how to use it, you can't remember not knowing how to use it because it's so easy... this is like a lot of programming, actually.

That's true. Probably why the teaching has such a knowledge gap.

Write yourself a git is a wonderful site to read through, even if you don't end up doing the implementation. It really helped my git usage!

But git isn't just for programmers, and cheat sheets can be good for people who just want to dip their toes in.


I think you are right about the DAG. Once I understood what the high-level data structure of git is, many branch related commands immediately made sense and it was suddenly very easy to use. Many of my colleagues haven't taken the time to learn that and continuously struggle with basic commands.

Honestly the DAG isn't the whole story. The distributed nature also adds a twist that makes things entirely difficult, and the obtuseness of the commands is yet another layer of difficulty. People have every right to expect git commit to commit their changes to the remote repo. And it very well could, but it just doesn't happen to. Similarly you can't tell me with a straight face that notion of having to 'git add' a file you just deleted in order to be able to commit that change is somehow intuitive. I could go on and on but the point is knowing the DAG is hardly the end of the story.

I agree it's not the end of the story, but without that knowledge, learning git is almost impossible. Even with DAG knowledge, some commands are hard to remember.

What's a DAG?

Directed Acyclic Graph.

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.



Unfortunately this article, like almost all others, is still wrong because it looks like commits get mutated when you rebase and the old commits disappear.

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.


Directed acyclic graph. Basically each git commit points to zero or more parent commits (usually one, zero for root commits, more than one for merge commits) and that forms a DAG.

The gatekeeping is strong on this one.

I don't get this "afraid of losing something" mindset at all. In fifteen years, I've "lost" some minor changes maybe 3 or 4 times, and this was mostly with SVN, which does not have the safeguards that Git has. The only thing that I am moderately afraid of is pushing to the wrong remote branch.

> I don't get this "afraid of losing something" mindset at all. In fifteen years, I've "lost" some minor changes maybe 3 or 4 times, and this was mostly with SVN, which does not have the safeguards that Git has. The only thing that I am moderately afraid of is pushing to the wrong remote branch.

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"??

> "I can lose something for you in 2 seconds _in_ git."

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.


Wow just tried this.. is this considered a bug or a feature and where can you read about this if it is intended behavior?

`git checkout --help` gives

       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.

Also note that 'widget' could've also easily been a typo for a branch (maybe they meant 'widgets' or something). Meaning that even if you only stick to uses of git checkout for branches, you're not safe (unless you're infallible).

Probably a feature given how many gazillion meanings they've given to checkout intentionally, but I have no clue. Hopefully it got the point across though ;) and I'm pretty sure it's not the only way to lose info in git...

In a near future, hopefully we will have two new commands, git-switch and git-restore. The former is only about switching branches, the latter restoring files. Then you can stay away from the overloaded git-checkout.

See

https://github.com/git/git/blob/pu/Documentation/git-switch....

https://github.com/git/git/blob/pu/Documentation/git-restore...


That's awesome :) though this is just one way to lose files.

Yeah some of those ways are known and there may be a way to recover those lost changes. It's still too early to see if something will materialize.

Apparently I'm warned when I do this using SmartGit; if you are unable to safely use the CLI tool, maybe you should switch to a safer tool and leave the CLI tools to power users?

> if you are unable to safely use the CLI tool,

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.


What you did there is the equivalent to a `revert` in SVN ... I'm not sure what the issue is? Why would you checkout `widget` and not expect local changes to be overwritten? Like, that checkout reverts is one of the first things you learn with git ...

> What you did there is the equivalent to a `revert` in SVN ... I'm not sure what the issue is? Why would you checkout `widget` and not expect local changes to be overwritten? Like, that checkout reverts is one of the first things you learn with git ...

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.)


> Why would you sudo rm -rf /

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).


I'm not being hyperbolic or giving a trivial example (whatever that's supposed to mean), I was completely seriously pointing out that people used to apply your exact same victim-blaming logic to that same scenario until others finally made them come to their senses and realize that, just because the user happens to type in a command that tells the computer to shoot him in the face, that doesn't necessarily mean the computer should actually go ahead and shoot him in the face. Somehow you managed to take those 6 words out of context and focus on bashing the example while ignoring the actual real problem I was illustrating with it in your reasoning.

A "trivial example" is where you demonstrate a concept using a simplified or trivial scenario. It's quite a common term, and I didn't mean it as a put-down.

"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.


It's not a concocted scenario, the cli command in question explicitly has a property that defaults to preventing it from doing what it should do, unless you override the property. Explicitly because it's so destructive when you do it by mistake.

it's concocted because that's something you'd never do.

git reflog

Honest to god, I don't know how people who find `git` hard to use manage to write code. Everyone on the Internet acts like the concepts are impossible to grasp and it's like really easy to grok.

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.


It's common for people teaching others how to use git to start off the lesson with some sort of disclaimer about git being really hard. I think this is a huge mistake. I suppose it's meant to prevent the student from feeling discouraged if they happen to struggle, but the student struggling with git is not a foregone conclusion. Such statements can demoralize the student before they even dive into it though. When you tell your student that git is hard, you are doing them a disservice.

I usually couch git intros with "git was designed for linux kernel developers to get their work done, specifically filesystem developers, specifically Linus, so a lot of things won't make sense unless you approach it from the mindset of the above. don't worry! that mindset is probably different to yours but you can learn a good approximation to it and then you'll be really good at reasoning about git"

Git being difficult has unfortunately become a meme, and like all memes, gets propagated irrespective of its truth value.

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.


I think many believe that "this should be easy, just add commits and get versioning, voilà!" and thus never invest the time (what, 2 days max?) to actually dig in, try a few common scenarios on a dummy repo, before actually using it on a live project.

The truth is, it's not so easy a task to get versioning right and git does, people are not humble enough.


I agree. Usually if someone gives out a concrete change they'd make to improve git I can quickly point out a simple workflow it breaks because they have a mental model of a Github repo with a trunk and branches. Lots want to couple commit and push, but on this machine

  history|grep 'git commit'|wc -l 
is 20 times larger than

  history|grep 'git push'|wc -l

I know some people know git in depth but for me its a tool that shouldn't be main the focus of my work. It should work and get out of the way (which it does for basic workflows). Its when something unusual happens that it is a pain in the arse to use and requires far too much understanding in my opinion.

Most of OP's post isn't just the basic Git commands - it's super-user features that might help a novice who can do the basic add/commit/push/pull chain but doesn't even know you can revert in Git history, or make new branches.

Nothing shameful in someone sharing that knowledge to help a newcomer become a super-user faster.


Nothing against the post. It’s sharing tips. I’m talking about what the other commenters are talking about. The ‘difficulty’.

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.


Confess. You are the Git Hobgoblin masquerading as a human programmer.

http://stevelosh.com/blog/2013/04/git-koans/#the-hobgoblin


I refuse. I object to “git is hard”. I do not object to “the UX can be improved, perhaps significantly”. Other things that are easy but could have improved UX:

* 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.


My guess is that git is a supporting tool to the main task. As such, it's tempting to always search for the next solution for a git problem on StackOverflow, or wherever, rather than actually learning the concepts behind git.

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.


yes. If it is this hard to understand rebasing, how can there be understanding of other programming core concepts?

I was thinking if I would post what you did it would get downvoted but yeah, if you find git hard how or why are you writing code? That is surely a lot harder. Not sure why it is downvoted as sure it might not be a popular opinion but it seems to hold...

> if you find git hard how or why are you writing code? That is surely a lot harder.

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?


Thank you for posting. It helps to know that others, too, haven’t had this uphill slog just to use it. It just faded into the background right from the start.

I want to focus on code and not be wrestling with a version control system.

OK hot shot. Tell everyone here how you've never fucked up commands and had to blow out a repo and start over and how were all idiots for having done that.

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.


> never fucked up commands and had to blow out a repo and start over

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. ;)


It's called sarcasm /s

Yes, I'm aware you were attempting sarcasm, but it wasn't very good, was it? I thought it would be kinder to ignore it and post something useful. The phrase you're looking for is, "thank you".

Cheers!


No hot shot either but that literally never happened. It happened with cvs but not with git.

And confusing can be and imho in this case is, something else than difficult.


I'm not the hot shot. The hot shots are all the kids from Berkeley and Stanford who figure this shit out as interns while supposedly fully-trained engineers with all the knowledge of data structures that should come with that think this shit is too hard.

I think I'd be completely unsurprised to see an intern successfully use `rerere` on a longer project of theirs.


I know of teenagers who happily use git because nobody ever told them they were supposed to consider it hard.

This is exactly what I’ve seen. Thank you for validating that viewpoint. Without being told they’re going to find it hard, they just rapidly build a model of how to interact with the tool and how it improves their life.



Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: