I believe the same, and it's not just a problem you see with git, it's all over the place. Some developers seems eager to use something so they just skim the documentation in order to do the least amount of reading and understanding in order to implement something, but often miss some fundamental detail and have to jump back. Or, they fundamentally misunderstand the tool at all, but push forward with their own idea what the tool is, rather than stepping back and start learning again.
That's especially true in technology, where whole armies of people are working to complicate things as fast as possible. For the best of reasons, of course. But when I started out, I could read one short book and have a pretty good understanding of my computer's processor, and from there it wasn't much further to understanding the whole computer. Now I could spend days just understanding, say, processor cache strategies [1]. A field that is super interesting, but if I am to get any of my actual work done, I can't afford to dig into that and the many, many other similar subtopics. I'm going to get a casual understanding of something, do my best to make something useful, and only dig in further when I'm forced to.
When I do have to dig in, it comes in two cases for me. One is where there is something necessary complexity that I would have to learn about regardless. E.g., if something is too slow, I need to learn about what happens under the hood to do proper performance tuning. Great, fine, I will learn it.
But then there's the other bucket, which includes unnecessary complexity, bad abstractions, poorly considered UX, and the like. For me, git is clearly in that bucket. I intentionally have very simple development flows. Git can do a great deal, 98% of which I not only don't need, I actively don't want. [2] So I'm going do my best to remain ignorant of its inner workings, stick with my small number of commands, and very occasionally refer to "Oh Shit, Git!?!" [3] And I'm perfectly happy with that until it gets replaced with a technology that better matches the domain.
[1] e.g.: https://chipsandcheese.com/2022/05/21/igpu-cache-setups-comp...
[2] An example of what I don't want: https://www.tiktok.com/t/ZTRpPPuKf/
Conflating the two, like you do, is elitism.
"If you just stopped and read to understand...", a-ha, sure, I'll do that for every one of the no less than 500 tools I've used over the course of my career and will never get anything done on time.
There's no time. We got real work to do and no we can't switch to a company that gives us this time. There's a big world outside the Silicon Valley.
GIT is a huge UX failure and seeing people pretend otherwise makes me question my -- or their -- sanity.
Obviously not everyone can learn it quickly. That's a fact. Denying it is ruining the forum discourse.
Git has dozens of failure modes like this where the behaviour is completely unintuitive unless you understand the internals of git.
[0] https://stackoverflow.com/questions/7244321/how-do-i-update-...
~/tmp/foo git init repo1
Initialized empty Git repository in /home/user/tmp/foo/repo1/.git/
~/tmp/foo cd repo1
tmp/foo/repo1 ‹master› echo hi > hi
tmp/foo/repo1 ‹master› git add hi
tmp/foo/repo1 ‹master*› git commit -m hi
[master (root-commit) d0cf572] hi
1 file changed, 1 insertion(+)
create mode 100644 hi
tmp/foo/repo1 ‹master› cd ..
~/tmp/foo git clone repo1 repo2
Cloning into 'repo2'...
done.
~/tmp/foo cd repo1
tmp/foo/repo1 ‹master› echo hi2 > hi
tmp/foo/repo1 ‹master*› git add hi
tmp/foo/repo1 ‹master*› git commit -m hi2
[master 3a82bdc] hi2
1 file changed, 1 insertion(+), 1 deletion(-)
tmp/foo/repo1 ‹master› git checkout -b someotherbranch
Switched to a new branch 'someotherbranch'
tmp/foo/repo1 ‹someotherbranch› git
tmp/foo/repo1 ‹someotherbranch› echo otherchange > otherchange
tmp/foo/repo1 ‹someotherbranch› git add otherchange
tmp/foo/repo1 ‹someotherbranch*› git commit -m "otherchange"
[someotherbranch 99ec880] otherchange
1 file changed, 1 insertion(+)
create mode 100644 otherchange
tmp/foo/repo1 ‹someotherbranch› cd ../repo2
tmp/foo/repo2 ‹master› git remote add upstream ../repo1
tmp/foo/repo2 ‹master› git fetch upstream
remote: Enumerating objects: 8, done.
remote: Counting objects: 100% (8/8), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (6/6), 448 bytes | 448.00 KiB/s, done.
From ../repo1
* [new branch] master -> upstream/master
* [new branch] someotherbranch -> upstream/someotherbranch
tmp/foo/repo2 ‹master› git checkout master
Already on 'master'
Your branch is up to date with 'origin/master'.
tmp/foo/repo2 ‹master› git rebase upstream/master
Successfully rebased and updated refs/heads/master.
tmp/foo/repo2 ‹master› git switch someotherbranch
branch 'someotherbranch' set up to track 'upstream/someotherbranch'.
Switched to a new branch 'someotherbranch'Also, not having another option would hardly make git optimal (or even good).
If you think you can do better, please do! Let me know when you've gotten a few projects to switch over and I'll gladly learn that, too. Not a lot of projects using mercurial these days.
And. totally agreed, the number should be growing (especially for such a nice piece of tech with a far better toolset). Now that you've signed on to learning, hopefully that will be the case.
Wanting to understand things before using them is hardly elitism, not sure why you would think that.
Just like you probably don't want to fix bugs without understand the cause, it's hard to use a tool correctly unless you know how the tool works.
Also yeah, I agree learning GIT is not a huge sacrifice but with time I built a huge internal resistance against it so... dunno. ¯\_(ツ)_/¯
Maybe I'll get to it one day, in the meantime I am OK relearning cherry-pick and a few others every time I need them. I don't know, it just doesn't make sense to me. Guess to this day I don't see why it had to be a graph DB.
From the source:
Here’s me being straight up loco and resetting the head of my working tree to a particular commit:
$ git reset --hard 5f1bc85
The –hard option says to erase all changes currently in my working tree, whether they’ve been registered for a checkin or not (more will be said about this command later). A safer way to do the same thing is by using checkout: $ git checkout 5f1bc85
The difference here is that changed files in my working tree are preserved.It's not obliterated. It is still in the reflog, and in the history of any other branches or tags then have those commits in their history.
You have to try quite hard or wait 2 months to obliterate something that was committed or stashed into a repo.
You can even recover changes that were only staged.
When I see a doctor, I expect them to be familiar with the latest medical research. I expect they will treat my illnesses with modern medicines and employ the right tools, correctly, and understand how they work at a sufficient level of depth to do the job correctly. For example, I used to make electrical medical staplers; surgeons need not care about how RTOSes work, but they need to know how to interact with the software enough to do their job. Similarly, I'm not saying we all ought to be able to build Git from scratch. I'm saying we ought to master it to the extent necessary. If your use case is committing alone on a single branch, learn "git commit". But for most devs, understanding the tree structure that Git uses to store data and what basic operations are doing "under the covers" builds a mental model that makes Git easy.
We have to have the time to learn the tools that help us do our jobs well. You can neglect that duty to do "real work", as much as a doctor can neglect learning how to use a scalpel so they can "get on with the surgery".
Even when the tools themselves suck?
The thing about our profession is that anyone can build better tools. Just like Linus took a week or two to sketch Git with some C code and a bunch of shell scripts (seriously, that was what happened - but he had the BitKeeper design in mind and how to improve on it). There is not regulation that says that in order to build a tool for millions of people to use you need to have a certification or anything.
Citing it as if it's something that's even needed to be said is kind of ironic on a forum with mostly programmers in it who, I am pretty sure, by and large possess a fair amount of critical thinking and quick analysis skills.
A ton of people have to make do with partial schedules. And I mean a ton, likely no less than 85% of all programmers everywhere.
You might want to make a good deep analysis on whether you're not coming from a position of severe privilege and a very positive filter bubble.
A better thing to say is "yeah we've been saddled with this horrible tool, yeah we know it sucks, but it'll suck a bit less when you learn it. Oh and sorry if you're not a professional developer and have to use git, I hope we can do better next time."
I say that as someone who uses git daily, who has learned git internals, who uses cli almost exclusively, who helps the teammates out of their git problems and who still hates the cli inconsistencies.
1) Making Computer Science programs in universities overly simple. When I was in college, I tutored students in CS. When I started college, the CS 101 course was taught in Java, and when I finished, it was being taught in Python. There was an immense gap between the understanding of the Python-first vs. Java-first students.
The Java-first students understood fundamental concepts like arrays, passing parameters by reference vs. by value, "OOP" concepts, and other common paradigms in languages. The Python-first students would use "lists" and "dictionaries" for many problems without understanding that those structures impacted the time complexity of their solutions, or that they used memory allocation, reallocation, hashing, etc. Python is great for hacking together a non-fault-tolerant program that does X as quickly as possible, but I saw it damage the way the students thought about computing.
It made the students think it ought to be easy. It made them think that they should be able to write, in nearly plain English, what they wanted the computer to do, and that it ought to do it. It made them think they should be able to intuitively be able to code without spending any amount of time learning the craft, and that they could do anything an advanced programmer could do by typing a few lines of simple code. They were less willing to accept that sometimes problems are hard, sometimes they would have to think, and sometimes they would have to write more than a for loop to solve problems. The Java students' problem sets covered implementation of data structures and complex applications, while the Python students struggled to put together even the implementation.
For a humorous and broader explanation of this subject, see James Mickens' excellent article The Night Watch [0]. For example: "That being said, if you find yourself drinking a martini and writing programs in garbage-collected, object-oriented Esperanto, be aware that the only reason that the Esperanto runtime works is because there are systems people who have exchanged any hope of losing their virginity for the exciting opportunity to think about hex numbers and their relationships with the operating system, the hardware, and ancient blood rituals that Bjarne Stroustrup performed at Stonehenge."
2) Bad documentation, and bad users of documentation. I dislike documentation that neglects how the tool works and instead creates a cookbook that its users can copy verbatim. It has similar effects to what I described above. Developers that use this type of documentation find themselves helpless when they encounter a problem they've not memorized the solution to. I think it also creates a learned helplessness wherein users cannot question the model the tool uses or think outside that model to solve complex problems. I prefer documentation that teaches the ethos of the subject in question so that I can understand better and imagine new solutions.
I've also heard the complaint that Git's documentation is awful; some say that it is only useful if you know already know where what you are looking for is, and useless if not. In other words, it lacks an "apropos" (aka "man -k") equivalent. This is my gripe with "wiki" style documentation. It is scattered and unsearchable and has no cohesion. We as developers need to do a better job creating documentation that is searchable, coherent, and useful to new and experienced users alike. Various sections of documentation should have ample cross-references to help users understand the connection between parts of the system to help them form a mental model of how tooling works.
[0]: https://www.usenix.org/system/files/1311_05-08_mickens.pdf
Edit: wording
A big reason I wanted to pursue this idea is exactly what you mentioned - a lack of ability to easily create presentation-quality Git structures that can apply specifically to the situation a user is in, NOW.
A super annoying thing about troubleshooting Git issues is when you find a similar solution online but it's done in someone's random repo which could be very different that yours. Your whiteboard sessions are another good example down this line of thought.
It's like teaching someone to drive a car using LOGO.
The way to teach Git is:
1. Explain the mental model (correctly). Basically a commit is a snapshot with deduplication to avoid huge file sizes.
2. Show them how to use a good Git GUI. There are a lot of bad ones. I would recommend Git Extensions (terrible name but it's actually quite good). Or maybe GitX on Mac.
3. Let them learn the CLI slowly in their own time.
I’ll take a look at Git Extensions, thanks for the recommendation! Some of our devs took to using the integration with VS Code, which I didn’t like because I didn’t understand how its terminology (e.g. “sync” mapped to underlying Git operations). Others tried the Git GUI which I found very confusing.
I focused on the graphical operations and then tried to show them what commands they could run to execute those operations. Next time I’m in the situation, maybe I’ll adopt a GUI-first approach.
[0] there is one operation where gui is more convenient for me - exploring branches in gui or GitLab ("repository -> graph") has no equivalent in cli that I know of (interestingly, GitHub doesn't have that either, but many gui tools do).
The first point is that it makes it easier to learn the concept of Git, which is most of the difficulty. Yes Git's CLI is really terrible, but if you know the concepts you can mostly just google the CLI options and learn through repetition.
The second point is that GUIs make it easier to see the state of the repo. There's a reason that even the CLI includes a basic GUI. Don't tell me you never use `git log --oneline --decorate=all --graph`.
The git CLI for the vast majority of things isn't difficult once you have a basic understanding of DVCS. 95% of the time fetch, pull, push, checkout and commit are all you need.
I admit that Hg had a far nicer an more consistent CLI, and it was my preference back in the day. But it lost for a few reasons - one of which was that it made the 90% easier, but the 10% far harder.
Github is largely to "blame" for this. It mostly only exposes the happy path and using git kind of like any other VCS, but above all it ossifies the idea of git repositories being centralized (both because the canonical repo is always on github.com but also because of the forking/ownership model). A lot of the power (and sharp edges, and messiness) of git are in scenarios where everybody has a local repo and nobody's is really "canonical".
Git is really well-suited to development of the linux kernel. It's not.. amazingly well-suited for the job that 95% of people these days press it into?
Tags could be utilised for naming commits which would make it much easier to reason about where in the process of your development you are instead of referencing commits by hash. Hashes are hard to remember after all.
Branches are so cheap in git that there's little to no reason to not use a million branches to name every step in a process if you need to, and yeah if you need a long-term name for a specific commit local-only lightweight tags are handy for that. (I find lightweight versus annotated an often easy enough distinction between tags intended for local development and tags intended for release markers. This also seems to be intentional in git UX because of the way `git describe` by default skips lightweight tags.)
The problem I have with git us that it's just too complicated. When I'm juggling project complexities and design complexities and implementation complexities, I frequently don't want to have to deal with git too.
I understand how to stage and check in and squash, and branch and merge, and how to manage remotes. But last week I still managed to get a new repo into a state where I couldn't push local changes to github without doing a pull first, and I couldn't do a pull without (apparently) first updating the remote. I had a hard deadline and ended up deleting the repo and starting fresh. Nothing much was lost. I never had this trouble with tfs...
Maybe I'm just not clever enough, but I've never come across a dev tool with as much surface complexity as git.
(Not a git hater. Use it every day. Have had a personal github profile for a decade. Etc etc.)
As for check-out locks. you only had to ask them to do that if they'd requested a lock on the file which was not the default. I hope people weren't doing that too often at your workplace.
https://www.bitquabit.com/post/unorthodocs-abandon-your-dvcs...
One of my fav rants on this subject.
And if you can't merge, you can't branch. And if you can't branch, you need locks.
I think, as other have mentioned, that once you truly "grok" git you begin to understand the elegance and simplicity of the system and how its layered. Ultimately git's use of branch pointers was a way better solution than encoding branch data in the commit itself. To it's credit Mercurial did attempt to rectify this later with bookmarks, but ultimately DVCSs are about branch management, and git has always excelled at it.
Many things in git, are frankly anti-patterns in any other sane versioning system - comitting partial changesets because of the index? gross. Rewriting history and force pushing? absolutely insane.
The fact that I don't have to "grok" mercurial like you need to git is actually a plus. Mercurial can do everything that git does (and more: check out changeset evolution in mercurial, it blows anything you can do with git rebase out of the water) and it doesn't require Ph.D. level knowledge in directed acyclic graphs to do it either.
`git merge --no-ff former-branch` by default includes "former-branch" in the auto-generated merge commit name. When it matters what branch a commit "originated" on, it's very easy to use `--no-ff` (which doesn't rewrite history and won't need any sort of force push!) and most PR tools used to default to that before "rebase" and "squash merge" became some people's favorites because of some weird (in some cases OCD) wish to see SVN-like straight lines only in their commit history.
Linear history is a punchline that ignores the reality of source control. The only truly "linear" history I'm aware of in source control is systems built on patch theory/operational transforms/CRDTs and that form of linear is subject to reordering and still needs explicit merge commits with (implicit) multi-commit dependencies even if there are few stated needs to visualize that as a DAG.
I agree with you, in general it is good to minimize complexity in life, which I why I eliminate rebase workflows and squash merges from mine. I prefer to see the evidence of where merges happened (and they should happen early and often), and graph data structures are very familiar to me as a software developer, including traversing them. (And git offers useful traversal tools such as --first-parent for when you want to view the DAG more like a linear history and ignore some of the noise. There's no reason to artificially simplify the DAG when DAG traversal is easy and accurate.)
Not that I don't do interactive rebases sometimes in local branches, but that I absolutely do not make them a part of any more general workflows. I rather try to avoid rewriting shared history where I can, and I'm happy with DAG navigation tools like --first-parent.
> Not that I don't do interactive rebases sometimes in local branches
I don't have a problem with rebasing in local branches before code review. I just don't allow rebasing once a branch has started code review, and I especially don't allow rebasing in a public integration branch ever.
(I also rarely feel the need to rebase, personally, even in my own local branches. Working in better tools than git in a past life has given me a lot of the discipline I feel that I need to build good commits as I work. I make much more use of the git staging area than I see most devs generally do and use `git add --patch` and `git add --interactive` far more than `git rebase`. `git add --patch` especially is an under-sung hero that I think a lot of junior developers should learn well in advance of rebase.)
Also, I'm just not that bothered by messy histories in code reviews when I review other developers work. If the idea comes across even if the commit messages don't have the greatest narrative flow or are perfect, I'm perfectly fine with that. I make sure all merges are --no-ff and after that I can browse a "clean" history post code reviews with DAG traversal tools such as --first-parent. But that also gives me an opportunity to dig back into the mess months or years later after the code review if I need to research a bug or a regression. That sort of "sewage archeology" isn't "fun" by any means, but the number of problems I've been able to solve with it is high enough I appreciate keeping that mess around in source control. It's source control's job to keep the mess for me and present me pretty views for more regular work. That's why I prefer to trust tools like --no-ff and --first-parent over --squash, because that's what source control is built to do, control old histories messy or not.
I love a code review to consist of lots of small commits. I'm mostly fine if even some of them from developers more junior than me are just named "commit" if they are small.
When I finish the code review I prefer to only `git merge --no-ff`. All those small commits stay in the code history in the exact form they were reviewed under.
If I'm looking at history I'm most often using something like `git log --first-parent`. In my --no-ff integration branch that just shows me an integrated change list (PR list). Git gives me a "straight line" view down the DAG and hides irrelevant information I don't want in that moment like all the small "commit" commits.
If for some reason I find a bug or issue in some code, I may need to drill down into specific small commits including the "commit" commits, and I have that ability because all those commits are still in the DAG.
* Trying to shorten commit history
* Trying to merge feature branches together (main or other feature branches)
* Trying to remove feature branches
None of which are necessary in a lot of development models. If you have that many feature branches that you have to rebase constantly, it doesn't sound like feature branches as a concept are properly understood.
The idea behind feature branching is one of 2 models:
1) either the main branch always works, and development is done in a feature branch, only to be rebased into main when development, testing, and sign off occurred, or
2) serious bug fixes occur outside the main branch, to be re-incorporated back into the main branch once it's been proven to be effective.
The 1st model should only require a rebase when the feature is complete. The 2nd model MAY require a couple of rebasing, dependent on how extensive the bug fixes are, and how fast the main branch is moving along, but not to the point where constant interactively rebasing your branches is the norm.
Whose workflow are you trying to emulate? (followup question: and why?)
I'm amazed how many people who have used SVN or CVS just assume git works like that but with different commands. And, of course, they get very screwed up. (GitHub contributes to this problem, too, with their own set of commands like "forks" and "pull requests" that aren't really a part of git.)
One you understand the Merkle DAG that's at the heart of git, understanding the rest is simple. Yes the commands are named badly, but get past that and it's a great system.
You can't even get a simple gitk-style graph visualization on Github. Which is a website. After so many years, I really think all the blame for developer confusion can be laid at Github's feet.
Arguably they buried it because it isn't actually what people want when they are looking for commit information. I know I've confused many developers by showing them gitk. I've been slowly working on a hypothesis that "subway diagrams" of commits look fantastic (Git Kraken looks great in screenshots) but get in the way of actually getting work done, much less thinking about the git DAG as an important tool beyond just "visualizing branches". (At this point I keep threatening to build a --first-parent based drill-down UI, that would look ugly as all get out in screenshots but might be a joy to actually use, but so far no one has sent me the check to make good on that threat.)
Yes, first-parent and expand would be a good way to view history. Github is a website and should be providing new and interesting ways to view information, not failing to provide even the basic lowest-common-denominator view (the gitk view).
We now have a large selection of tools that allow you to visualize what's going on (I use git-kraken), as well as google for help on doing something that isn't in muscle memory.
But, really, SO MUCH pain and suffering could have been resolved by refactoring the damn commands to something more coherent and consistent. Git people sometimes call this "porcelain"-- which I guess is an apt name because it makes one think of a toilet (but an improvement to a hole in the ground).
it's like tar or rsync or ffmpeg. Yes, it's hard to keep track of all the command line flags etc. but thanks to the internet we don't need to. it's far more useful to understand the underlying concepts.
If the cli "matched up" with a visual way of thinking, for many of us, the cli would become second nature. But it just doesn't match up. It's fugly and needlessly hard to remember and become fluent in at least for us "incompetent" engineers who already have too much to work on.
Also it's foolish to suggest that one of the 21st century's most complex repository management software is just like tar, or rsync. What planet are you from?
Git Kraken is excellent, though Git has a page on various GUIs, many of which are free with no restrictions: https://git-scm.com/downloads/guis
Personally, on Windows I like SourceTree: https://www.sourcetreeapp.com/
Some that have worked with SVN back in the day like TortoiseGit: https://tortoisegit.org/
On *nix Git Cola seems to do the job for me: https://git-cola.github.io/
Then again, the most complex workflow I've worked with was Git Flow and I didn't need anything more advanced than that. Come to think of it, I don't really do rebases often either and mostly just take advantage of squashing commits through GitLab/Gitea and such, when needed.
But hey, that's also valid, using Git in a way where you get version control but mostly keep the technical details out of your way (though Git LFS and certain cases with particular line endings being needed does make you drop down occasionally).
It's almost pure happenstance that Linus's idea of Git being distributed lent itself to the Cloud hosting business model. And the hardest thing to teach is the mental mapping of local and remote refs.
So this isn't just Git being too complicated. It's a confluence of several factors that would never have been possible if Git had never happned.
But I do think that there's room for a better mechanism for orchestrating "macros" or building other workflows on top of git. I know a lot of developers who'd love the ability to pretend they're using p4.
I learned git at 45yo after many years using ClearCase (even licensed as CC admin) and .. I really liked learning git, I do admit that it took me two full days of self study to start groking git but I wouldn't call it 'struggling'.
Obviously it depends a lot of your background.. git is the 5th version controls I've used , while it is far from being perfect (submodules.. :-( ), it's my favourite.
Though that's only true of the core, with tags being additional decorations and renames inferred. The simple core delegates complexity.
I think designing a nicer git UX is not easy. Even if we oversimplify commit to cp your working directory together with a path to the previous commit (to form a tree of "branches"), then the abstraction for operations like rebase to manipulate that tree is not quite intrinsically simple.
BTW Tools can be used without proper understanding, like arithmetic without commuting, associating or distributing. While a workable pidgin aids adoption, it's fair to argue for the power of understanding, especially of something clean and coherent and whole. Git's core is like that; the add-ons (like tags) aren't; and the UX seems arbitrary and accreted.
I believe the core issue is devs like above never take the time to “grok” Git. I believe part of this is due to the lack of a tool like above to teach easily, but also because devs want to ignore the “how” of their tool working to find out the exact commands they need to run in their current situation.
One could argue users should not need to understand how their tool works to use it effectively and this is a leaky abstraction. Others could argue some commands are not intuitive (git reset can do many things, and git rebase swapping ours/theirs comes to mind). I don’t claim Git is perfect, and sometimes I need to Google to learn more than I wanted to, to get out of an obscure scenario (e.g. someone put classified intelligence into a repository we need to transfer to a lower classification network; how do I rewrite the entire history while correcting this error that occurred 487 commits ago?). However, understanding the tree structure of Git, what git rebase does under the covers, what branches and tags are, how remotes work, how merging works, etc. would go a LONG way. The folk I worked with struggled to pick it up.