Tech blog writers, please stop invoking the "UNIX philosophy". It's always used as a pseudo-technical argument replacement by people with subjective opinions about why software X has too many useful features.
(author here) I want all those features! I just want them to be separate commands, like "eg" does. "eg switch" and "eg unstage" will never clobber the working tree.
Even the traditional Unix tools don't follow the Unix Philosophy extremely well. ls is a prime example: It has too many ways to sort and format its output. It "should", in the Unix Philosophy sense, simply list things in a directory and leave sorting and formatting to other tools.
However, parsing the output of ls is generally a bad idea, as it's meant for human consumption, so ls breaks another tenet: It isn't very composable, as it is difficult to use it in a pipeline, even though it should be a prototypical source program.
I don't even bother using 'find' to actually find files. 'du -a <path> | grep <name>' works easily enough and I don't have to remember find's weird syntax.
I'm not sure there's much upside in using -delete, -exec etc. from find since they're redundant and can be replaced by composition; half the time, I want to stick a grep -v in there between the found paths and the execution, so xargs is usually handier than not. (I have a tool called print0 which converts line terminators to nulls, so I can use xargs -0 along with grep etc. without needing to use -print0.)
I'd argue there's a difference between plumbing commands and UI commands. ls is strictly a UI command, a sort of mini-language to quickly and efficiently explore the file system. If ls didn't exist as a binary, we'd all have an ls bash function in our .bashrc that does the same thing with some perverse combination of for, globs, and stat. If you ever pipe the output of ls to anything (barring very simple cases), you're probably doing it wrong and should switch to find -exec stat instead.
Try to replace ls with something that acts subtly different (or just give one precedence in your path) for a few weeks and you'll see a surprising amount of stuff calls out to ls.
I agree it's a bad idea (or if you do, at the very least make sure you're calling out to /bin/ls...), but it certainly happens.
The original authors of the Unix tools agreed back at least as far as 1983 that the traditional suite was growing new features inconsistent with the Unix Philosophy. Rob Pike's "cat -v Considered Harmful" presentation and associated paper are classics:
I agree that over use of a phrase can dilute a message but I think you're not acknowledging something important.
The phrase "Unix Philosophy" is a concrete, functioning example of compositionality.
Git is a pretty heavyweight tool and many of its commands are not compositional or built up from many smaller functional units. Git repos also do not compose, which is a significant flaw.
I think there may be better role models but I have a hard time finding ones that are jargon-free (to a degree, "pipes" and other terms are jargon but generally accessible to people who work with Linux), approachable, and functional for people that do not have the mental model.
Unix has produced a bunch of composable tools that have stood the test of time - find, grep, sort, uniq, wc, sed, awk etc. - decades of reusability and composability, things I reuse and compose to this day. I can't honestly say that about any other computing tool I use.
The Unix philosophy references some concrete properties of software (many processes performing small tasks, communicating with text streams), but these are neither desirable (serialize/copy/deserialize is wasteful and kills locality; process boundaries preclude inlining and other optimizations) nor really core to compositionality. The compositionality of processes and pipes is arguably the best thing about Unix, but doesn't even work terribly well: piping n different inputs into the same command requires hacky workarounds, and you end up writing ad-hoc parsers/text-manglers constantly.
One way to cast the ideological stance of the Unix philosophy is "simplicity of implementation at all costs"; the costs include memory unsafety, poor performance (of pipelines and shell scripts), code duplication, race conditions (look at the filesystem, a vast blob of global mutable state!). It's time we start fixing these problems and abandon the mistakes of Unix. Yes, composition is important, but you don't have to sacrifice so terribly much to get it.
I agree with you on your technical points; however, I still think it's a good analogy to use until something better with reach enters users minds.
Perhaps there is a better concrete example I haven't thought of. I'm curious to know what you think would be a better communication tool (this a serious question, I'm not baiting an argument).
I really agree with this, it's too late for git now, but I consider the overloading of 'checkout' to be one of the worst pieces of UI I've ever come across, and the source of confusion for many people when I try to teach them git.
I didn't know that 'git reset' has a similar problem.
If git gained the 'checkout-tree' and 'checkout-file' (and other) commands mentioned, I'd personally use them instead of checkout.
This is my go to page for untangling the behaviour of git reset and checkout: https://git-scm.com/blog/2011/07/11/reset.html . And I find myself going to it quite often because another problem with this kind of slippery design is it just doesn't stick, in your head.
(Amusingly, the author begins by admitting he wrote a whole book on Git - and perhaps the most popular one - before strongly understanding this himself.)
It's not really too late for git, if there is sufficient energy around rolling back the change. The relatively standard process for API changes of (1) adding a new command that does the same thing for a major version, (2) adding a deprecation warning and description of the new way to do it (say 'Please use checkout-tree instead') for another major version or two before (3) it stops working in major version 3. That allows people to get used to a syntax change without breaking scripts for a while. The git project has already used that process for other API changes. In fact, it's pretty common practice to do that sort of thing when a given CLI option starts getting too baroque and complex.
Git is really 2 tools in 1: a manual Distributed File System, and a Version Control System built on hashing. And much new vocabulary. Instead of helping to make the system simpler and easier to understand, the vocabulary actually makes the system more complex and increases the slope of the learning curve.
It shouldn't be so complex to indicate you've copied files from Github, made a personal copy to work on, and have switched to that copy. But this involves finding where your Head is, having an Origin, a Master, and somewhere Remote, finding the right branch in the tree to hit with a fork, and going to a checkout counter.
Agreed. When reading this I realized how I avoid the path-usages of checkout, specifically because it breaks the whole checkout model. The only exceptions is `git checkout -- .` (which I treat like a separate command that happens to use the same words).
Okay, now I'm curious, what does `git checkout -- .` do?
I consider myself fairly good with Git, having used many (but nowhere near all) of it's more advanced features, some of them regularly - but this is a new one to me.
The double dash typically indicates all CLI flags/options have been provided, and the remainder should be treated as positional arguments. However, a single . isn't any standard CLI flag, so shouldn't need the --?
Argh -- good catch. You're right, `git checkout .` does the same thing as `git checkout -- .` (discard unstaged changes), and I'd been using the latter for a while before I realized, so I usually use the longer one out of habit.
Hey, it happens! I'm confident some of the flags I provide git daily do nothing meaningful either ;)
Edit: I also googled it, as I thought up a reason why -- might be used. If I have a file called master, and want to unstage my changes to it, I would have to `git checkout master`. Given this command has multiple meanings, both of which could be appropriate, I'd need to disambiguate between them. The -- does exactly this, it prevents the tree-ish parsing.
Back in 2013, I was working for one of Redfin's competitors, near Redmond. We were moving the developers off VisualSourceSafe, and the DevOps expert (hired at the same time as me) was a Git pro, so we tried Git.
Git lasted almost a week. The last straw was when a junior developer accidentally deleted the main line repo. We switched to TFS and never had problems like that again.
Presumably because permissions were not set correctly. The poor fellow was tasked with compiling 20-year old code with a completely circular-linked library. He spent months hacking it into shape, and made great progress.
But this problem continues to occur. Last year, working on an Ops team, team members started noticing their scripts were no longer present on production servers. It turns out a team member was modifying code and checking it in. The problem is that Git clobbers unchanged files, so he was unknowingly reverting changes.
After years of seeing numerous professional developers and engineers continuously have team-impacting, show-stopping issues using a tool, I must assume the tool is defective in some way.
> Presumably because permissions were not set correctly.
Which, of course, is also potentially a problem with TFS, and a developer can similarly wipe out the repo if permissions allow them to.
I'll grant you that it's easier to go so with Git by doing something like push -f, so it's more important to have permissions right in that case.
> The problem is that Git clobbers unchanged files
Except it doesn't? I'm not sure what was broken in your case, but that's definitely not normal or expected Git behavior, and I can't think of any time I've seen anything like that. About the only case I can think of where Git insists on "modifying" random files is when line endings are mismatched, and the repo is not configured properly. But even then it's not lossy.
> Except it doesn't? I'm not sure what was broken in your case, but that's definitely not normal or expected Git behavior, and I can't think of any time I've seen anything like that. About the only case I can think of where Git insists on "modifying" random files is when line endings are mismatched, and the repo is not configured properly. But even then it's not lossy.
Git does this by design. There seems to be no concept of line-item uploads - either you have to push the whole fork or none of it. There seems to be no concept of "I haven't modified this file", and an assumption that a user will frequently be fetching and aware of the entire code base.
Some tools help with this awareness, by auto-fetching frequently, so you know when others have uploaded code and you need to merge the master back into your branch. (Err diff the mainline against your development copy, so you can integrate the upstream changes into yours.) Mandatory workflows like this should be automated as part of the tool, but here they are very clearly left manual.
If the developer is working in their own branch, as is the normal scenario with Git, then they can push that branch to their heart's content - nothing is magically changing there. They will, of course, need to merge it with the primary branch eventually - but that's the thing, you merge branches (or rebase them), you don't just replace one with the other... and it's not like the latter is easy to do accidentally. And when you merge, of course, it will only merge the diffs, so the concept of "I haven't modified this file" is inherent. If your fork has only modified the files that no-one else touched, then it doesn't really matter how old it is - it'll still be able to merge; you don't need to be frequently syncing with the main branch.
If the developer is working directly in the main branch, then what you describe can happen - although even then, if someone else makes changes there, there would be a divergence, and push would simply fail, instead of overwriting those changes (unless you explicitly force it by using -f... but the whole point of having that switch is to be a safeguard against destructive operations).
So I'm still confused about the exact scenario here.
Git wrappers are interesting things. I know a lot of people dislike them in concept.. but strangely enough, I like them in concept. I'm far from a git expert, frankly I'm just barely literate, but I firmly believe there must be a more simplified UX for Git.
Again, never to prevent normal usage, but to focus on educating the various states of git, and really trying to make the commands very obvious for normal use cases.
I see so many of these.. I'm wondering if it's sort of a rite of passage. Maybe I should write my own? I'm sure I'd become more proficient at Git if I did.
edit: Since writing this, I think I decided on writing my own wrapper. Specifically, focused on a very small subset of straight forward commands with exceptionally obvious wording and step by step education.
I really like the way http://www.git-town.com documents their command usage, so I'll likely adopt that - but with the added feature of providing either inline or copy-paste commands to gain per-step documentation. Explaining the motive behind a command, and why it was used in that case. I also dislike how git-town layers their own UX, as I think an education wrapper's goal should be to improve education on normal git flows, not make new ones (automatic syncing, etc). Wish me luck lol.
I rarely ever use the Git CLI anymore. Instead, I use Magit, an Emacs extension. It is, hands down, the best Git tool available. Things that are cumbersome on the CLI become painless, like staging hunks of files, interactive rebases, viewing a blame of the file I'm currently looking at, quickly amending a commit that isn't the current HEAD, reverting parts of commits, browsing stashes, partially applying stashed changes, and much more. It is the single best tool I use every day aside from Emacs itself.
> quickly amending a commit that isn't the current HEAD
How? I use Magit too, and my go-to method for doing this would be an interactive rebase that only modifies the target commit. I'd love to know about a quicker way!
Ironically I find wrappers more complex because I constantly worry if they did everything correctly. That, and for every X people who understand raw "git" commands, X/2000 understand wrappers out of the box.
Funny enough, I've got some team members that I'm having to babysit with Git. Frankly, I shouldn't be babysitting anyone lol (due to lack of Git knowledge myself). Yet, I am. So I think a Git wrapper (as I mentioned above) ideally should do a very small set of things that set those developers off in the right direction. Basically what I verbally say every time they ask me what to do in X scenario.
With that said, I definitely agree with you. This thing I'm discussing writing is a wrapper for the non-git users. I don't expect what I'm going to do to actually save normal/advanced users time, I expect those users to not even use a wrapper.
I wonder if git wrappers should always print the corresponding git command they are actually executing before doing anything. This might give the benefit of using the wrapper with a bit of passive understanding of the actual corresponding git commands.
> Today, I specifically want to call attention to two commits by the lead maintainer of Git, Junio Hamano, that left the deepest scars.
Why call the maintainer out by name (and continually do so throughout the essay)? What purpose does that serve? To embarrass him for volunteering his time on an open source project that you've built your business on, just because you didn't like these two features he added? There was no personal behavior of his at play; he simply made some technical choices you disagree with.
Keep it technical, dude. This is ad-hominem and smug.
That empty, rhetorical cliche doesn't mitigate the way the author calls out the maintainer in such an abrupt and unneeded fashion in the first paragraphs. The author hadn't even described the problem before blaming the person; instead, were just told that git has 2 self-inflected, deep "scars", and that a single person, and by implication, that person's poor decision-making, is to blame for those scars.
Naming the maintainer would make sense later on when the author purportedly tries to get into contact with Hamano but, spoiler alert, fails at it.
If anyone might have done the same thing, why call one person specifically in the first place? This just gives the wrong impression and makes that person more reluctant to make changes.
I dunno, I think it's fine -- the article never attacks Hamano or calls him a bad person. Criticizing a person's decisions on technical grounds isn't an ad hominem argument, whether you mention them by name or not.
The author personalizes (literally) the problem: The problem is that Junio Hamano made these bad decisions, Junio Hamano wrote this code, Junio Hamano made git worse. And what about the code reviewers, the other people he discussed with, others who could have improved the system? No, it's all Junio Hamano's fault.
Elsewhere in codeland we usually say "this code is doing the wrong thing" not "Bob did the wrong thing."
In the section titled "You or I might have done the same thing" the author says "if I were Hamano, I probably would have made the same mistake". This is about the complexity of growing a new kind of distributed system where best practices are still unsettled. Specific commits and contributors being identified is part of taking this subject seriously. If you are above having your work questioned then you should not be contributing to a big critical part of open source infrastructure.
I still don't see a problem with this. Why should Junio not be responsible for the issues in his code? There is no shame in taking deserved blame. Project maintainers are usually pretty used to that, and they're typically their own worst accusers.
It's not like someone as storied as Junio, who has maintained git for as long as its been a serious project with applications beyond the kernel, risks being fired because he made a handful of questionable decisions. He is, after all, only human. Only the insecure fret over every shed of blame.
It is, in fact, considered rude to circumvent/bypass/ignore the original author by saying "the code was wrong" without first giving the author a chance to explain the rationale.
Ultimately, the point at which "naming" transitions into "shaming" in a technical discussion is a matter of interpretation and balance, but I don't think we need to completely remove considerations of code parentage from technical discussions, which you seem to be advocating.
As counterpoint, I didn't interpret it that way. The tone wasn't shaming a programmer for accidentally screwing up (e.g. It was Michael who deleted everything with UNIX command "rm -rf /* ").
Instead, today's blog was extending the conversation of the message he sent 3 months ago[1] and he had already put Junio Hamano's name in that post.
If you're trying to trace the intellectual history and rationale for why things were done a certain way, attaching a name to it is very common and it does not mean malicious intent.
I didn't see any ad-hominem attacks in the post but I can only guess that the repetition of "Hamano" in the text rubs people the wrong way.
Completely unprofessional and childish. I stopped reading the article after that.
This was posted on Redfin's official blog by a "Principal Engineer", which is even worse and reflects poorly on Redfin. Does Redfin shame engineers by name when things do not work out? If a higher up is reading this you should rethink your engineering blog ASAP.
i took it as maybe an attempt to try to catalyse change upstream in Git.
i hope i have a commit so controversial someone blogs about it 13 years later. i'd be equally happy to respond with a defense or concession. :)
it struck me as an irrelevant fact- not an attack. so i can't help but wonder if you all aren't taking more offense than the guy who made the commit himself.
It's not just that he calls him out for it, but that he does it so early on the piece before any real context is given, including what the actual problem is. We're just told that git is severely fucked, and that the problems can be isolated to 2 commits, even though the author fails to make contact with the commiter to give him a chance to make a defense.
> Well, I posted a question on the Git mailing list to ask, but the Git mailing list is so noisy that I doubt that anybody will ever answer my question, least of all Hamano. But I can speculate as to why; if I were Hamano, I probably would have made the same mistake.
And no, posting to the mailing list, which the author himself admits is too noisy for the commiter to have likely noticed, is not a real effort to make contact given the severity and self-assured nature of the assertion, which suggests to me that the author is lazy in other ways that make his analysis suspect. Seriously, the author is so cocksure about the problem, and yet too timid to email Hamano, who is on Github and has a listed email address?
It's an appalling lack of basic courtesy, never mind intellectual curiousity.
(Author here.) Let me begin by noting that your comment, criticizing my courtesy, calls me "lazy," "cocksure," and "timid." Name calling is not compatible with the basic courtesy you're calling for.
Despite this, I'll respond to you with basic courtesy.
As a maintainer of open-source projects myself, I hate it when people email me directly with questions. I ask that everyone use only public channels of communication, if only because that way everyone can benefit from hearing the answers. In my opinion, I showed courtesy and not "timidity" by not spamming Hamano's personal inbox.
However, I recognize that not everyone feels this way, and so I have taken your point and I have emailed Hamano directly. (I hope he doesn't mind.) If he responds either on the git list or to me privately, I'll update the post.
There's also the broader context that naming people that way is a signature of the online callout culture, where the goal is shaming and humiliation. This is so common now that it's likely to be the default frame when people run across it.
That wasn't the impression I got. He didn't attack Junio just basically said why he didn't agree with the direction things are going with a fair amount of technical detail.
Probably wasn't worth calling out the maintainer by name (or the hyperbole in the title, but clicks!) but at the end he explains that reading a interview with Junio how Git evolves and isn't really designed.
Even has a solution to the Git problems he sees "easy git" (which I had never heard of).
(Author here.) Let me begin by saying that this post is intended to be historical. If you've ever wondered why "git checkout" both switches branches and restores working-tree files, or if you've ever wondered why "git reset" both rewrites history and unstages hunks, this post has the answer.
History is about people making decisions. It wouldn't make sense to write about the history of nations without naming any names, and I think it wouldn't make sense here, either.
Second, while I am critical of these historical decisions, I don't purport to be better than Hamano. There's a whole section of this post saying that "you or I might have done the same thing" for the same reasons.
I especially don't accept your charge that this is "ad hominem." I don't argue that you should agree with me because who Hamano is. I don't (and wouldn't) call Hamano any names, call doubt on his character, or even question his intelligence or competence.
Everyone should evaluate these historical decisions on their own merits. But anyone who wants to understand why git works the way it does must understand that these are Hamano's decisions, and decide for themselves whether these decisions were good or bad.
EDIT: A number of comments here have said that the complaint has to do with the number of times I mention Hamano's name, so I've updated the post to remedy this.
I think parent took their criticism a bit far with "ad-hominem and smug", but the calling out of a specific individual makes me uncomfortable also.
The issue, I think, is that the specific name doesn't really add anything to the discussion: you don't actually contact Hamano and get his perspective, and you don't use the name to link any additional relevant information. You could omit the name entirely without detracting from what you're trying to say.
Including the name makes the (public!) criticism needlessly personal. Not really cool.
> History is about people making decisions. It wouldn't make sense to write about the history of nations without naming any names, and I think it wouldn't make sense here, either.
Historians usually do quite a bit more research, including talking to participants. Much more context would be required before zeroing in on one person and saying it's his fault. Was there discussion beforehand? Why has no one changed it thus far? All you did was post to a noisy mailing list from which you expected no reply, then assumed that it was solely the committer's decision.
> I don't (and wouldn't) call Hamano any names, call doubt on his character, or even question his intelligence or competence.
But that's done implicitly when you tie his reputation up so thoroughly with choices that you wrote an entire essay disparaging. The damage is magnified when he's not given a real opportunity to explain himself. Although you stated in another comment you've now emailed him directly, which is a good call.
Hamano replied pointing me to this thread. https://public-inbox.org/git/Pine.LNX.4.64.0510171814430.336... Linus proposed adding the feature to git-checkout, subsuming git-checkout-index. Junio expressed concern about file/branch ambiguity; Linus wrote back, "Yes, I know it's ambigious at times, but it really is very convenient. Usually we allow a "--" to say where a filename starts when it is ambiguous."
I think a separate command would have been convenient, too.
¯\_(ツ)_/¯
The author makes the argument that these commits changed the direction of git, and "Thanks to these two commits, it's now a tradition." The author's argument is made more compelling by the fact that it was the lead maintainer that made these commits. Did he need to name the lead maintainer? Perhaps not. But doing so does not convert the reasonable argument to an ad hominem fallacy.
"It's like Unix!" people say. "rm doesn't warn you, either!"
Reminds me of one of my common complaints about that response. The difference is that 'rm' doesn't do anything but remove stuff---it removing things isn't surprising. If a command has multiple potential effect types, and only some of them are destructive, then good UI would offer a warning.
For me the UX issue was that I came from "svn" and "svn checkout" is a VERY different command. Obviously this is not git's fault. I suspect git is much easier to learn for those untainted by svn.
As for "git checkout branch" and "git checkout -- branch" doing very different things, I find the "--" works well to bump my mental model into the right state, and so in practice I'm never confused about the distinction.
It also helps (a lot!) that "git status" teaches us about these modes for "git checkout --" and "git reset HEAD":
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: pom.xml
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes)
modified: src/main/java/com/bitbooster/FoxtrotHook.java
Not if they were never committed in the first place. It sounds like if you made changes without committing them (new files or modified committed files), and then branch switch, that content is gone with no warning.
Git usually aborts any checkouts or merges that destroy the working directory. You can however use `git reset --hard HEAD` (which I aliased nuke) and it will happily oblige.
You can (for example) modify a file, stage it, then further modify it. You now have three copies of your file - the original, the staged version, and the working tree version.
A "git checkout -- file" would revert your working tree copy to your staged copy, whereas a "git checkout HEAD -- file" would revert both your staged copy and your working tree copy to the unmodified copy.
There are other scenarios but this is by far the most common. The index (aka. staged files) can be thought of as a completely seperate copy of your working tree that can have arbitrary changes written to it independent of the working tree. It's just that in normal usage you're only ever copying to the index (via git add) FROM your working tree.
As a counterpoint, I like both 'checkout' and 'reset' as they are. Changing them for aesthetics would break a lot of scripts. Additionally I find the commands easy to use and remember.
It would be nice for a "git 3.0" to introduce a few new commands with consistent separation of responsibilities (new terminology as needed), and deprecate the twisted ones for removal in "git 4.0".
Whatever is confusing today will only be worse after more years of piling stuff on top.
95 comments
[ 3.2 ms ] story [ 219 ms ] threadHowever, parsing the output of ls is generally a bad idea, as it's meant for human consumption, so ls breaks another tenet: It isn't very composable, as it is difficult to use it in a pipeline, even though it should be a prototypical source program.
last week i discovered the `-delete` flag. For years, i had been doing `find <stuff> | xargs -rm` instead.
I agree it's a bad idea (or if you do, at the very least make sure you're calling out to /bin/ls...), but it certainly happens.
http://harmful.cat-v.org/cat-v/
Have a look at libxo, which tries to fix this exact issue. FreeBSD adopting it in the base system is encouraging. I hope Linux follows suit, as well.
The phrase "Unix Philosophy" is a concrete, functioning example of compositionality.
Git is a pretty heavyweight tool and many of its commands are not compositional or built up from many smaller functional units. Git repos also do not compose, which is a significant flaw.
One way to cast the ideological stance of the Unix philosophy is "simplicity of implementation at all costs"; the costs include memory unsafety, poor performance (of pipelines and shell scripts), code duplication, race conditions (look at the filesystem, a vast blob of global mutable state!). It's time we start fixing these problems and abandon the mistakes of Unix. Yes, composition is important, but you don't have to sacrifice so terribly much to get it.
Perhaps there is a better concrete example I haven't thought of. I'm curious to know what you think would be a better communication tool (this a serious question, I'm not baiting an argument).
I didn't know that 'git reset' has a similar problem.
If git gained the 'checkout-tree' and 'checkout-file' (and other) commands mentioned, I'd personally use them instead of checkout.
Those 2 commands would be a great idea.
(Amusingly, the author begins by admitting he wrote a whole book on Git - and perhaps the most popular one - before strongly understanding this himself.)
It shouldn't be so complex to indicate you've copied files from Github, made a personal copy to work on, and have switched to that copy. But this involves finding where your Head is, having an Origin, a Master, and somewhere Remote, finding the right branch in the tree to hit with a fork, and going to a checkout counter.
I consider myself fairly good with Git, having used many (but nowhere near all) of it's more advanced features, some of them regularly - but this is a new one to me.
The double dash typically indicates all CLI flags/options have been provided, and the remainder should be treated as positional arguments. However, a single . isn't any standard CLI flag, so shouldn't need the --?
What am I missing? :)
Edit: I also googled it, as I thought up a reason why -- might be used. If I have a file called master, and want to unstage my changes to it, I would have to `git checkout master`. Given this command has multiple meanings, both of which could be appropriate, I'd need to disambiguate between them. The -- does exactly this, it prevents the tree-ish parsing.
This is news to me.
What a vacuous statement. If you use something incorrectly, it works incorrectly.
But what's nice is you can also (usually) revert your Git oopsie.
Still, I don't usually run into the problems described because I try to keep Git as simple as possible.
Git lasted almost a week. The last straw was when a junior developer accidentally deleted the main line repo. We switched to TFS and never had problems like that again.
But this problem continues to occur. Last year, working on an Ops team, team members started noticing their scripts were no longer present on production servers. It turns out a team member was modifying code and checking it in. The problem is that Git clobbers unchanged files, so he was unknowingly reverting changes.
After years of seeing numerous professional developers and engineers continuously have team-impacting, show-stopping issues using a tool, I must assume the tool is defective in some way.
Which, of course, is also potentially a problem with TFS, and a developer can similarly wipe out the repo if permissions allow them to.
I'll grant you that it's easier to go so with Git by doing something like push -f, so it's more important to have permissions right in that case.
> The problem is that Git clobbers unchanged files
Except it doesn't? I'm not sure what was broken in your case, but that's definitely not normal or expected Git behavior, and I can't think of any time I've seen anything like that. About the only case I can think of where Git insists on "modifying" random files is when line endings are mismatched, and the repo is not configured properly. But even then it's not lossy.
Git does this by design. There seems to be no concept of line-item uploads - either you have to push the whole fork or none of it. There seems to be no concept of "I haven't modified this file", and an assumption that a user will frequently be fetching and aware of the entire code base.
Some tools help with this awareness, by auto-fetching frequently, so you know when others have uploaded code and you need to merge the master back into your branch. (Err diff the mainline against your development copy, so you can integrate the upstream changes into yours.) Mandatory workflows like this should be automated as part of the tool, but here they are very clearly left manual.
If the developer is working directly in the main branch, then what you describe can happen - although even then, if someone else makes changes there, there would be a divergence, and push would simply fail, instead of overwriting those changes (unless you explicitly force it by using -f... but the whole point of having that switch is to be a safeguard against destructive operations).
So I'm still confused about the exact scenario here.
Again, never to prevent normal usage, but to focus on educating the various states of git, and really trying to make the commands very obvious for normal use cases.
I see so many of these.. I'm wondering if it's sort of a rite of passage. Maybe I should write my own? I'm sure I'd become more proficient at Git if I did.
edit: Since writing this, I think I decided on writing my own wrapper. Specifically, focused on a very small subset of straight forward commands with exceptionally obvious wording and step by step education.
I really like the way http://www.git-town.com documents their command usage, so I'll likely adopt that - but with the added feature of providing either inline or copy-paste commands to gain per-step documentation. Explaining the motive behind a command, and why it was used in that case. I also dislike how git-town layers their own UX, as I think an education wrapper's goal should be to improve education on normal git flows, not make new ones (automatic syncing, etc). Wish me luck lol.
https://magit.vc
P.S. - to those worried about what git wrappers are doing behind the scenes, in Magit you just press `$` to see the command log.
How? I use Magit too, and my go-to method for doing this would be an interactive rebase that only modifies the target commit. I'd love to know about a quicker way!
With that said, I definitely agree with you. This thing I'm discussing writing is a wrapper for the non-git users. I don't expect what I'm going to do to actually save normal/advanced users time, I expect those users to not even use a wrapper.
Why call the maintainer out by name (and continually do so throughout the essay)? What purpose does that serve? To embarrass him for volunteering his time on an open source project that you've built your business on, just because you didn't like these two features he added? There was no personal behavior of his at play; he simply made some technical choices you disagree with.
Keep it technical, dude. This is ad-hominem and smug.
Naming the maintainer would make sense later on when the author purportedly tries to get into contact with Hamano but, spoiler alert, fails at it.
Elsewhere in codeland we usually say "this code is doing the wrong thing" not "Bob did the wrong thing."
It's not like someone as storied as Junio, who has maintained git for as long as its been a serious project with applications beyond the kernel, risks being fired because he made a handful of questionable decisions. He is, after all, only human. Only the insecure fret over every shed of blame.
It is, in fact, considered rude to circumvent/bypass/ignore the original author by saying "the code was wrong" without first giving the author a chance to explain the rationale.
Ultimately, the point at which "naming" transitions into "shaming" in a technical discussion is a matter of interpretation and balance, but I don't think we need to completely remove considerations of code parentage from technical discussions, which you seem to be advocating.
As counterpoint, I didn't interpret it that way. The tone wasn't shaming a programmer for accidentally screwing up (e.g. It was Michael who deleted everything with UNIX command "rm -rf /* ").
Instead, today's blog was extending the conversation of the message he sent 3 months ago[1] and he had already put Junio Hamano's name in that post.
If you're trying to trace the intellectual history and rationale for why things were done a certain way, attaching a name to it is very common and it does not mean malicious intent.
I didn't see any ad-hominem attacks in the post but I can only guess that the repetition of "Hamano" in the text rubs people the wrong way.
[1] https://public-inbox.org/git/EA993AC0-022C-423D-ABD7-4747FA0...
This was posted on Redfin's official blog by a "Principal Engineer", which is even worse and reflects poorly on Redfin. Does Redfin shame engineers by name when things do not work out? If a higher up is reading this you should rethink your engineering blog ASAP.
(sorry, it's a huge pet peeve of mine, every technical article has to be an ad as well)
i hope i have a commit so controversial someone blogs about it 13 years later. i'd be equally happy to respond with a defense or concession. :)
it struck me as an irrelevant fact- not an attack. so i can't help but wonder if you all aren't taking more offense than the guy who made the commit himself.
> Well, I posted a question on the Git mailing list to ask, but the Git mailing list is so noisy that I doubt that anybody will ever answer my question, least of all Hamano. But I can speculate as to why; if I were Hamano, I probably would have made the same mistake.
And no, posting to the mailing list, which the author himself admits is too noisy for the commiter to have likely noticed, is not a real effort to make contact given the severity and self-assured nature of the assertion, which suggests to me that the author is lazy in other ways that make his analysis suspect. Seriously, the author is so cocksure about the problem, and yet too timid to email Hamano, who is on Github and has a listed email address?
It's an appalling lack of basic courtesy, never mind intellectual curiousity.
Despite this, I'll respond to you with basic courtesy.
As a maintainer of open-source projects myself, I hate it when people email me directly with questions. I ask that everyone use only public channels of communication, if only because that way everyone can benefit from hearing the answers. In my opinion, I showed courtesy and not "timidity" by not spamming Hamano's personal inbox.
However, I recognize that not everyone feels this way, and so I have taken your point and I have emailed Hamano directly. (I hope he doesn't mind.) If he responds either on the git list or to me privately, I'll update the post.
Probably wasn't worth calling out the maintainer by name (or the hyperbole in the title, but clicks!) but at the end he explains that reading a interview with Junio how Git evolves and isn't really designed.
Even has a solution to the Git problems he sees "easy git" (which I had never heard of).
"Come work with us! We'll only be sure to publicly humiliate if you make a technical decision we disagree with!"
History is about people making decisions. It wouldn't make sense to write about the history of nations without naming any names, and I think it wouldn't make sense here, either.
Second, while I am critical of these historical decisions, I don't purport to be better than Hamano. There's a whole section of this post saying that "you or I might have done the same thing" for the same reasons.
I especially don't accept your charge that this is "ad hominem." I don't argue that you should agree with me because who Hamano is. I don't (and wouldn't) call Hamano any names, call doubt on his character, or even question his intelligence or competence.
Everyone should evaluate these historical decisions on their own merits. But anyone who wants to understand why git works the way it does must understand that these are Hamano's decisions, and decide for themselves whether these decisions were good or bad.
EDIT: A number of comments here have said that the complaint has to do with the number of times I mention Hamano's name, so I've updated the post to remedy this.
The issue, I think, is that the specific name doesn't really add anything to the discussion: you don't actually contact Hamano and get his perspective, and you don't use the name to link any additional relevant information. You could omit the name entirely without detracting from what you're trying to say.
Including the name makes the (public!) criticism needlessly personal. Not really cool.
> History is about people making decisions. It wouldn't make sense to write about the history of nations without naming any names, and I think it wouldn't make sense here, either.
Historians usually do quite a bit more research, including talking to participants. Much more context would be required before zeroing in on one person and saying it's his fault. Was there discussion beforehand? Why has no one changed it thus far? All you did was post to a noisy mailing list from which you expected no reply, then assumed that it was solely the committer's decision.
> I don't (and wouldn't) call Hamano any names, call doubt on his character, or even question his intelligence or competence.
But that's done implicitly when you tie his reputation up so thoroughly with choices that you wrote an entire essay disparaging. The damage is magnified when he's not given a real opportunity to explain himself. Although you stated in another comment you've now emailed him directly, which is a good call.
I think a separate command would have been convenient, too. ¯\_(ツ)_/¯
Reminds me of one of my common complaints about that response. The difference is that 'rm' doesn't do anything but remove stuff---it removing things isn't surprising. If a command has multiple potential effect types, and only some of them are destructive, then good UI would offer a warning.
As for "git checkout branch" and "git checkout -- branch" doing very different things, I find the "--" works well to bump my mental model into the right state, and so in practice I'm never confused about the distinction.
It also helps (a lot!) that "git status" teaches us about these modes for "git checkout --" and "git reset HEAD":
Quick quiz, what does: git checkout x/y do? At least:
* Reset a file called x/y
* Check out a branch y from a remote x
* Check out a local branch called x/y
Moreover, to mention the person's name ~10 times is extremely crass.
Is there anything truly destructive in git though? Unless there's a GC, you can still recover any unreferenced objects.
A "git checkout -- file" would revert your working tree copy to your staged copy, whereas a "git checkout HEAD -- file" would revert both your staged copy and your working tree copy to the unmodified copy.
There are other scenarios but this is by far the most common. The index (aka. staged files) can be thought of as a completely seperate copy of your working tree that can have arbitrary changes written to it independent of the working tree. It's just that in normal usage you're only ever copying to the index (via git add) FROM your working tree.
Whatever is confusing today will only be worse after more years of piling stuff on top.