What’s wrong with Git? A conceptual design analysis (2016)
blog.acolyer.org
blog.acolyer.org
The inconsistent cli and naming can be pasted over with cleaner GUIs. No, the real issue is gits configurability. Not just the configurability but the enforced decentralization of that configurability.
For better or worse, it seems like git is actively hostile to any kind of concept of a central authority. Can I configure hooks for my team? No. Can I configure diff tools for my team? No. Can set recursive pulls to be the default? No. Can I enforce line endings? No. Can I even enforce LFS is setup right? No.
I certainly can't even enforce things in receive hooks and expect team members to know how to change their histories to adhere to our policies.
I love git but its such a pain.
I really wish someone would come up with some kind of "accept remote config" policy or some such thing.
Perforce for code? It tries.. but, no thank you. Even if the client was good (it's really not, and will spin a CPU core when it detects a network hiccup) -- it still has a frustrating workflow paradigm, p4ignore is configured per-client, everything is read-only (leading to ugly context switches unless your editor directly integrates with p4) and the command line is so terrible that I think it was actually a joke that someone ran with.
No, perforce is not what you want, without even going into the "decentralised vs centralised" aspect of it.
I wish it were public.
Still though, P4Merge is my go-to diffing tool.
"Branches", the way we use them in git are more like perforce Shelved CLs.
Fwiw the only major issue I've had with branching is the fact its a full fat copy of your source which can end up taking up a load of space depending on how large your application is post build and how many binaries you dump into perforce.
They seem to be pushing towards "streams" now, but I've heard they make it easier to create development branches but haven't worked with them to find out.
p4 branch <name>
(fill out the mapping)
p4 integrate -b branch //...- Large binary asset support.
- Exclusive locks for binary assets.
- Speed. When configured properly, it's very fast, particularly for large repositories.
- Tooling support. It's nearly ubiquitous in gaming.
I'm working on repositories that are 100+ GB and need to support both programmers and artists. Git just isn't the right tool.
We do use git for backend server-side projects. Having to support two different version control systems is a pain, but, again, it's useful to have the right tool for the job.
GitLFS supports locking too.
Ironically, what files count for p4ignore isn't even centrally configurable. You can't configure diff tools or merge strategies centrally either.
Because every commit has to hit central, receive hooks are actually useful and you can do big branch remappings in a more seamless way than adding a git submodule.
I forget where line ending configs live.
P4 solves some of these things but not everything and adds its own headaches.
1. Write and distribute a wrapper script that sets up the clone in the way you want, with hooks, diff tools, LFS, etc.
If you're talking about diff tools and LFS, you need some way to actually install those components, so presumably it shouldn't be hard to get this wrapper script onto end user machines.
My employer uses this and it works well - we have a special command that creates a new clone and configures it appropriately. Once you have a clone, you can use the git commands as usual. People seem fine with this workflow. (Previously the special command was named something like "git-xyzclone" so you could just run "git xyzclone" instead of "git clone". But as we added a few more build/review/release features, we made a general "xyzdev" command with a few subcommands, of which "clone" is one.)
2. Set up /etc/gitconfig (or ~/.gitconfig) and in particular the init.templateDir setting to customize the settings you need.
Again, presumably you have some way to install things - but if you don't, tell employees to run "curl -O https://corp.example.com/.gitconfig" as part of setup, and then you can bootstrap yourself from there.
Git doesn't and cannot accept executable hooks/LFS helpers/etc. from an arbitrary remote server because then as soon as one of your employees clones something from GitHub they'll get event-stream'd. Your authority to enforce configuration comes from your authority (either technical or social) to get employees to install things on their machines - if you have that authority, then there are multiple ways to accomplish this.
(I suppose Git could have a "core.executeArbitraryCodeFrom=https://git.example.com" setting or something, but if you have the ability to push out that setting to all your users, you have the ability to push the actual settings you want.)
I understand the sentiment but I feel like this is just not true. Its designed to share code you plan to build and run, after all. As you say, it seems like we could solve it with signatures and trust but I doubt we'll ever get there, sadly.
In fact you can set the default branch to contain nothing but those two files, and have the setup script switch to the real branch once it's done with its work.
(And if your users have some other way they expect to run code - make, setup.py, yarn run, whatever - you can use that instead as the entry point.)
This is pretty good advice. If projects expect onboarders to run one-time scripts to install third-party dependencies, the least they can do is to also provide scripts to setup the development environment.
I still think git should have a solution for a consistent default configuration if features are going to be supported as opt in configurations.
That would be a security vulnerability. Hooks in git are not sandboxed, so you'd have arbitrary code execution in the machine of anyone that pulls your repository. In fact, some of the worst security issues the git project has had were ways of tricking it (usually through case-insensitive or normalizing filesystems) into setting arbitrary hooks.
https://www.viget.com/articles/two-ways-to-share-git-hooks-w...
Granted, each team member must set core.hooksPath in their config.
Hope this helps.
Use CI to verify the commits fits the styles and review the commits before merging.
You can't fault a tool for not providing a centralized authority when it's very central design principle is being decentralized.
.gitattributes text setting
That's one down. Thank you very much.
There are hooks for pulling and whatnot. This is a vector for malicious code execution.
Not everyone has a GUI system (window system).
Git _has_ to be able to expose low level commands while still being usable.
Decentralization is a _good thing_ when it comes to git. It is directly one of the goals of open source software, for which Git was built.
You can configure all of these things for your team provided they run an init command at least once. Then set up your system however you like via hooks that automatically run to update their configs as necessary.
Remember, your development process is not my development process. Also, not every system that clones a repository wants to run hooks. Git shouldn't force them to.
You can 100% enforce line endings, please research. You just have to configure it in the unit command as mentioned above.
If your team members are not adhering to your policies, then you're not managing them well. It's not git's problem to do your job for you.
For those of us who use git on a daily basis across a wide range of environments, repositories, usecases and websites, git is a dream to use.
Really tired of the "git hate".
Well it could help you in that, and we know that because other VCSes do. Have you managed a large inexperienced team with an offshore component? You are herding cats and there is never enough time to look at everything.
Yep! The engineers we had worked fine like this with just Git, github issues and slack at the time. I've left the company but I know that slack didn't suit them well so they switched to something else.
They swear by git still though. It works fine.
This is true. But the number of devs who work entirely in shell is dwindling as basically everything transitions to IDEs.
> If your team members are not adhering to your policies, then you're not managing them well. It's not git's problem to do your job for you.
I totally disagree. There is a reason why we use linters to enforce style guides. Tooling and automation always helps, even if you have strong engineers who are trying to do the right thing.
You're in a bubble then.
Git has lots of configs that are automatic and shared and that you can locally override yourself such as the ignore and attribute files. Why not more? I have no issue (and in fact prefer) if clients can override the projects configuration but git should support a turnkey workflow, at least for its own features. As it stands, you can't add submodules seamlessly, for example.
Its not about adherence. Workflow as Simon-Says is just a bad workflow. This stuff should take zero cognitive load.
But there is a workaround: centralising the .gitconfig in a repo, then symbolically linking it to your home directory, so it gets updated on every pull. I haven't done the experiment, but you might be able to do the same with your .git/hooks.
If need be, you could even cron a git pull in that repo, or add it to your .bashrc, $profile for PowerShell or schedule a task.
I agree, it is a bit of a pain.
Game development is pretty much on the opposite end of the spectrum, 99.9% of game project data isn't text (maybe not by number of files, but definitely by file size), and game development also traditionally isn't as extremely decentralized as Linux kernel development.
The problem isn't git, but that it has become so popular that it's now the defacto standard version control system, and people expect it to work for scenarios it wasn't created for (at least people who don't know much about the specific workflow requirements of large-scale game development).
...this is not the case for most projects. They want to have a clear source of truth for the code.
Almost none of these thinkpieces about git are saying that Linus was foolish in his approach or that he should have anticipated git becoming the lingua franca for junior engineers at megacorps. Instead almost all of them are talking about git's failing in modern development and bemoaning the lack of a better option, followed by a bunch of people insisting that git is actually awesome as long as you can reason about graphs.
I think your criticism here is actually implicitly already part of almost every discussion about git in modern times.
It just happens so that most software development is more like the Linux kernel and less like game development, so for most software projects git is a good fit indeed, just not for gamedev. Maybe it's worth looking into how CGI studios are managing their data, I guess they have the same problems to solve as gamedev studios.
There are specialized solutions though, like https://www.plasticscm.com/ (quite recently acquired by Unity).
I don't think that is true. Linux kernel development is truly distributed. It is expected that there are multiple completely deployed branches. Most software development using git does have a single point of ownership and a single release branch. It is done on company machines with managed environments.
This is an instance of a more general problem in all software development: tools are created usually to serve a purpose, but when they become popular they end up being used, not because they're a good fit for the problem, but because they're popular -- everybody knows how to use them. The tradeoff (which may be worth making) is the time to learn a new tool vs. the continual cost of using a tool that's not quite right for what you're doing.
Well, that'd be a huge negative to me… it is much easier to build up a commit piece by piece, verify that it's correct, and then commit it. The only two alternatives I can imagine are worse: disallowing partial commits (I use this feature all the time; I guess I could replace it by a temp commit on a branch and then so long as cherry-pick -p still exists, use that) or specifying it to git-commit (that would be one gnarly set of flags…)
Sure, a git without a staging area is less complex. But it removes essential complexity, and would no longer solve my problems.
(And in systems that lack it, like Perforce, I've sorely wanted it, and there was just not a good workaround. But yeah, that aspect of the VCS was simpler, I suppose…)
I do agree teaching the staging area seems problematic. Newcomers struggle with it. I also agree with how the article mentions other commands sometimes effecting one or more of the staging area & working dir, and it being unclear when are where they effect what. `reset` is perhaps the most confusing command in that regard. Outside of "git reset", "git reset -p" and "git reset --hard", I'm looking at the manual while I run it. (Granted, those first 3 cover probably 99% of the use cases…)
Are you aware that git commit --amend exists? That replaces anything I might ever be tempted to use the staging area for.
No, hg amend -i is much worse than git add -p. I do that when I have to use mercurial and there's significantly more mental overhead to make sure I'm not accidentally modifying a commit I did not intend to modify. Creating a "temporary" commit before I have a complete change is also annoying.
And once you need to add a file, you're back to having a staging-like concept.
IMO the staging area is fundamental complexity.
As I see it, the staging area is useless because it's basically a weird not-quite-commit-thing that you are more or less required to deal with (no way to opt out of it--several commands end up mucking about with the staging area even if you're not trying to use it). It feels like the better way to model it is to make it an actual commit, let it be manipulated as a regular commit, and then potentially improve the tooling around manipulating unpublished head commits as well.
So, sorry, staging being "fundamental complexity" sounds petty BS to me. You like it, and that's fine, but it's in no way fundamental. I've used git for a long, long time, tried staging a few times and have never reached for it again.
I think I'll stick with staging.
So ideally, IMHO, the staging area should work exactly in reverse of what it's currently doing.
Once that's done I'll start a script that verifies that all the incremental commits individually pass tests (and maybe grab a coffee). While the tests run I'll do a separate pass over the commits.
For me the staging is absolutely crucial, and I can't imagine wanting to go back.
Instead of having all the changes in your working directory and commit some temporary state not reflected in the working directory, I'd like to move my changes into a temporary area and then pick the lines into my working directory and commit that.
That way I can compile and run tools etc on the code that will be committed before committing.
If I'm not ready to commit everything, I could then stop the "commit mode" and my uncommitted changes would be brought back into the working directory.
At least that's what I dream of.
So the flow would be:
<working on big change churn churn churn>
> git stash
> git stash apply -p
<pick some things to have in working tree>
> make test
> git commit -a -m "First change!"
> git stash apply -p
<pick some more things>
...repeat...
?I could get behind that, and I personally like the index.
Since Git has stash, it feels like my idea is mostly a frontend thing that shouldn't require much backend changes, if at all.
git add -p # Review and stage
git commit # Create commit
git stash # Save uncommitted changes
run_tests.sh # double-check
git stash pop # or drop
I'll do something like occasionally, but mostly I just rely on the CI to double-check me.CIs on large teams can't run fast enough to check every individual commit. They have to run them in bulk. So the CI will often not catch that of your 2 consecutive commits the first one breaks if the 2nd one fixes that break.
Not investing time or resources into making your CI fast and reliable is a false economy IMO.
One way to achieve this is by putting the changes into stash 'git stash push', then interactively grab the desired hunks from the stash into the work dir with 'git checkout --patch stash@{id}'. This will also add the accepted changes into the index, staging them for the commit.
Kinda granular, but allows me to keep those nice verbose debugging parts yet out of commit.
I like it a lot.
This only works if you use a GUI like Sourcetree . With the command like staging isn’t much fun.
I do agree a GUI lowers the barrier to entry.
And you'll go straight to the patch option. Then you can split the chunks further, and for hunks that can't be split further, you can edit them and add/remove lines from the hunk as needed.
Its one of the nice features of Perforce but they screw it up by only allowing separation at the whole file level.
What I found myself doing is using a plain old commit instead of a staging area. Where I would previously build a commit in the staging area before I finally create it, I now simply keep amending it until it suits my need. It oftens starts out with a message like "WIP". This also scales to building multiple commits (use --fixup instead of --amend, and checkout instead of stash).
In practice the workflow is very similar and I was surprised by how little the removal of a core feature that I used all the time actually affects me.
The problem with staging is I want every file I changed in one place so I can notice what changed and look through the diff together. Not to just show up at the bottom of the list .
People like to tell me, "Oh see you just need to understand that git has something called a 'directed acyclic graph' blahblahblahblah" and no, that has nothing to do with it. It's just incredibly arcane and bizarre syntax for the most obvious everyday operations. But since my scripts make life easy, I sort of don't care at this point.
Also I don't rebase and thus I don't force push and thus I don't reduce my repo to a pile of burning rubble and work til 3AM repairing it and if people ask me to rebase enough times I'll threaten to literally take a dump on their desk/car/front porch which usually makes them stop. I advise doing this as well.
I actually don't care so much about seeing the messy history, but its annoying when everyone on a team is always rebasing there branches and force pushing them. I've definitely been at places where one team member is waiting on another to merge their code to master so they can start working.
Of course, rebase all you want locally before you push.
But I imagine there exists a design for a source control system where you can preserve history and also provide a clean interpretation of that history.
For example, why does rebase have to be a destructive operation?
Additionally, "preserving detailed history" makes cherry-picking fixes across multiple release branches a royal pain: not everyone has the luxury of having on single, ever-green release branch. So having a feature/fix in a single, self-contained commit that hides away the actual history is a good thing in this scenario (with the added bonus of not running into fluff/half-cooked commits when running git bisect)
Let's say there's a master branch at commit M1. You branch off of it. You develop your feature branch. In the meantime, master is at commit M26. Now on your feature branch all your tests pass, maybe even there's no merge conflict with the current master. But! Master's behaviour changed and you have no idea about it, and no idea whether it affects your branch. You merge feature to branch and kaboom!
Frequent rebasing protects you from this.
If you're already willing to do that then with the same amount of work you get cleaner history with rebasing, i.e. no intertwined branches.
There are pretty few operations in git which can't be undone by a quick glance at git-reflog.
? Well, git is supposed to be that 'history' for us. I understand it's not exactly a backup, but it is pragmatically that. I don't want to have a 'backup of backup' I want to feel confident with what's there.
To recover from commit screwups, it should be enough to just export the git repository somewhere else like a remote repo, or even just apply changes in a separate branch.
It just seems that a developer is doing something extremely wrong with their repository if they can't recover from their experiments by checking out a branch or commit.
I don't blame you, and I'm actually an enthusiastic proponent of this approach. It's just more ergonomic. I personally have an extensive list of idiosyncratic aliases that perform (conceptually) high-level operations on top of my git repo to let me work faster and use up less mental power to reason about what I'm doing. I use them at home and have carried them from one job to another (and am planning to do so indefinitely), taking care to keep things bash-based and portable between OSes.
I barely ever drop down to using built-in Git operations directly, besides `git push`. I don't need to, and I don't want to, even though I know enough about git to untangle complicated rebase clusterf*cks by reasoning about and operating directly on the DAG if I have to.
It's like that old saying about democracy: "Git is the worst version-control system, except for all the others."
I used to use Git Extensions and lately I've been using the one built into IntelliJ's products. It's not as good, but it's available on mac, so my options are limited.
I used to have such anxiety, but not any more. The final piece of desired state was revealed when I found the reflog -- curiously not mentioned in the article nor by any other comment here.
Try
git log --stat --walk-reflogs
in some repo where you've previously renamed some branches, or done 'git commit --amend'. All the transitional state changes are exposed, and can be undone individually.Fill out the toolkit with 'git branch -avv', and 'git status' with its various options, and you'll be ready to recover from almost any local mishap.
What about a git rebase with conflicts that you were doing interactively and is going to squash some of the commits?
In the first scenario I've learned to very carefully tread the golden path: make changes to the conflicting files to bring them to the state you want (I do this manually because it's less mysterious), and then "continue" the operation (usually I do this through the GUI; when I have to use the CLI I have to look it up). What would happen if I tried to commit something in here? Or unstaged and restaged something? Or stashed? Or pulled? Would it stop me from doing those things, or would I end up in some new weird state? I have no idea; I just avoid all anomalies and hope it goes smoothly and I end up back in a good place.
Just as one example, automatically stashing and unstashing when switching branches is so obviously convenient, in hindsight I'm puzzled as to why git doesn't just do this automatically.
So whatever happened to this project?
In the same way SourceTree adopted git-flow with a few special menu commands, I'd love to also see it be able to adopt a "gitless" mode.
Because the great thing about this model is that it can still remain the more complicated git underneath. It just makes using it more straightforward.
Yeah, I too have wished for this. Do note that that can fail, if there are conflicts, so you'd have to decide whether to fail the command (like today) or to drop the user into conflict resolution.
I personally found Mercurials a lot more palatable but at this stage git is pretty much ubiquitous.
A week later I was in the middle of coding when one of the nontechnical people from the company came by my cubicle, I motioned for him to wait a minute for me to finish what I was writing before talking to him, which he did. When I finally looked up, "okay, what's up," he said "first, let me see if I got the gist of this diagram. Inside of a folder you have this repository file named .hg..." and proceeded to give me a half-good explanation of how Mercurial works. And I was just floored and I said "yep, yep, there you go," and at the end he was like "so why do we save all of these things in Fileserv with a timestamp at the end?" and I was like "well I'll send you a link to TortoiseHg and you can just start using it, but yeah most people just want to do the thing that's in front of them and don't want to learn a whole tool that does things differently to get started." (And sadly that was true and as far as I know he also never invested the time to learn TortoiseHg and get started with that, haha.)
Left an impression on me, for sure.
I've thought about writing a stand-alone TUI version for people who are scared of emacs. But then I realise it's impossible. Magit is inseparable from emacs. Emacs is what makes it so powerful. Emacs is not a text editor, it's an environment for interacting with text-based tools. Having your text editor and git, as well as every other text tool, in the same environment with the same interface is unbeatable.
What I really want at this point is somebody to build a mercurial frontend for the git backend, so that I get to use the clean frontend of mercurial to work with git repos. No, hg-git doesn't really work for this purpose, and I have tried it for my work git repos (which are in the 100k of commit range).
Apparently, there's a new git extension for mercurial that is closer to the model I want, but I haven't had a chance to try it yet.
With git, the repo is basically just a DAG plus refs, and once you understand that model, it mostly makes sense.
hg has... three? four? different ways to rewrite history, that all work in different ways under the hood. It's not that bad once you realize you should just use the evolve extension and ignore everything else, but as a novice it's hard to know which method to use, and it's not hard to stumble upon people recommending awful footguns like `rollback`.
That leads to my next point about ignores. I hate that I have to manually untrack a file even if it would be ignored according to a new ignore file. The best practice there in my opinion would be to explicitly exclude said file IN THE IGNORE FILE ITSELF from being ignored. The other problem with ignore rules in Git is that for some reason Git tries to hide the fact that it has a per repo ignore file in the .git folder which doesn't get synced at all.
Instead of having untracked/tracked files, I would really rather just use ignore files, both local and remote, so that excluding something from being committed is an explicit action.
`git checkout -- <file>` checks out the HEAD version of a given file.
A:
`git reset --hard` checks out the HEAD version of your worktree.
`git checkout -- <file>` checks out the HEAD version of a given file.
B:
`git reset --hard` resets your worktree to the HEAD state.
`git checkout -- <file>` resets a file to the HEAD state.
I don't think a newbie could even grok the difference between these three descriptions.
git reset --hard HEAD
Does in fact only modify the work tree, since updating the current branch to HEAD is a nop.> I don't think a newbie could even grok the difference between these three descriptions.
This part is definitely true - Git offers only a very light abstraction on top of its data structures.
git-reset - Reset current HEAD to the specified state
git-checkout - Switch branches or restore working tree files
reset works primarily to modify HEAD, and when you include `--hard` it's like saying: oh and btw also update the working tree.checkout works primarily (in the case of checkout <pathspec>) to copy file from somewhere (commit, index) into the working tree. You can use checkout to mimic a hard reset: `git reset --hard` is pretty much equivalent to `git checkout HEAD .`
Nobody really needs to know about `reset --hard`, the same effect can be achieved with a normal reset and a checkout, it's a two step process anyway, regardless of how you do it.
Using reset+checkout is not the quickest but gets the job done. Power users can create themselves a shell alias, or write themselves a small shell script, or learn about one of the thousands commandline switches that does just what they need.
If there's something inconsistent about this, it's that it works for individual files, but (fortunately) not for clobbering your entire worktree by checking out a branch.
Isn't that `git reset --hard remote/branch` ?
What’s wrong with Git? A conceptual design analysis - https://news.ycombinator.com/item?id=12785200 - Oct 2016 (109 comments)
What’s wrong with Git? A conceptual design analysis - https://news.ycombinator.com/item?id=12777074 - Oct 2016 (1 comment)
What’s Wrong with Git? A Conceptual Design Analysis - https://news.ycombinator.com/item?id=6983566 - Dec 2013 (74 comments)
[1] Actually not the last one. I couldn't figure out what they were talking about. "Only committed versions of a file can be part of a branch" -- I mean... duh? Branches are a DAG of committed trees. I don't understand what they're asking for when they complain that the working version of a file can't be part of more than one branch. It's not part of any yet!
In other VCS tools, including gitless apparently, your uncommitted work is also branched (usually by virtue of different branches being chekced out in different directories on disk). You can also achieve this in Git by using different working trees.
I mean, literally "git commit -a -m WIP; git checkout $OTHER_BRANCH" is all that's required. It's even "orthogonal" per the requirements in the article.
I'd like Git to have a mode where, say, git rebase -i won't show me published commits by default, only work-in-progress ones. (This isn't necessarily the same as "commits since the remote branch we're tracking," since it's possible to end up tracking a branch that's behind for some reason, or even in a situation like a detached HEAD where you're not tracking anything at all.)
I'd also like Git to keep track of whether I'm done with a commit and ready to publish it. Usually when I commit to switch branches, it's because I'm thinking about the branch I'm switching to, so I don't want to spend the time yet to write a three-paragraph commit message about my current work. But that makes it too easy to push an incomplete commit message.
Don’t tags satisfy that use case?
Also, I don't think I've ever seen a repo where every pushed commit is tagged, and I suspect that will make lots of git operations that look at all refs scale poorly. The use case is that if I make a branch off current master, and then I run "git commit --amend" thinking I've already run "git commit," it shouldn't let me modify that commit - whatever it might be.
You can’t modify commits. They are immutable. Tagged commits are reachable so they won’t get gc’ed.
> Also, I don't think I've ever seen a repo where every pushed commit is tagged
Sorry, I thought we were talking about releases (aka important commits). I’ve certainly seen plenty of repos where all the releases are tagged.
I also think you should be able to have a bunch of WIP commits at once and stage changes to any of them equally easily (fixup/rebase but easier and by default).
> When I read comments like this, I feel sad. Between the lines I always read a resignation to design choices that create unnecessary accidental complexity amd are an obvious step backwards compared to other version control systems, even ones that came before. Competing systems like Mercurial, Plastic or fossil are a simple proof by existence for this claim
And then IMMEDIATELY:
> Yet there is some snobbery/mysticism around git
To clone Mozilla Firefox, Mercurial requires you have more than 2 Gigabytes of RAM. Not Firefox, Mercurial. Simply because the version history is so large, it requires over 2 Gigabytes of working memory to properly interact with enough to put a current clone of the repo on your computer.
`git clone --depth 1 <url>`
Of course you mean, only one small repo -- but the more git-like strategy is to specifically use submodules
You learn what a "branch" is in git and then you don't have problem. The problem with the linked article is that it's trying to understand a "branch" to mean something like a "working area" or a "checked out directory" or something. And that's just a different mental model. And getting to the criticism in the linked article, it's a very non-orthogonal model, polluting the idea of a "working tree" (which in git just means "the filesystem contents") with weird metadata about where the files "came from".
I really don't think that's the right interpretation. This is a direct criticism of the correct mental model for Git. In essence is says "This model, although functional, is way more complex than it needs to be. Here is a simpler model that accomplishes the same goals with less confusion for beginners and experienced users alike."
I don't see a great argument for the concept of un-committed changes existing at all, and the tool would be a lot easier if they didn't. My implementation would be for every local change to be immediately amended onto HEAD, and then `git commit` creates a new empty commit for subsequent changes to attach to. If you want to not include a file, exclude it. Or something like that, I haven't thought about it in detail really.
And I don't see how that statement is consistent with:
# First Understand "branches are commits"
$ git commit -a -m WIP
$ git checkout $NEW_BRANCH
I just don't, sorry. Trivial concept. Trivial composition of two commands you use all the time. Orthogonal. Clean. Straightforward. There are areas (c.f. the index, which I talked about upthread) where git does make excessive demands on the new learner. This is simply not one. Sorry.What the linked article wants to see is objectively a more complicated UI based on an incorrect understanding[1] of what a git branch is.
[1] Which is weird, because git's idea of "working on a branch" is fundamentally unchanged from the legacy handed down from SCCS, RCS, CVS and subversion. NONE of those tools ever did this!
And the alternative in something like P4?
cd ../other-branch
# do some work
cd ../first-branch
Vs the git solution: git commit -a -m WIP
git checkout other_branch
# hopefully you didn't have any untracked files that you meant to add on both branches
#do some work
git checkout first_branch
git reset -s first_branch~1
Sure, it's not THAT hard, but it's still much harder than it needs to be, it's harder to learn, easier to mess up and takes far more time (git checkout is not an instantaneous operation, unlike cd). Not to mention, there are at least 4 ways to do this, with slightly different trade-offs, further increasing the mental load.Edit: oh, and let's not forget that sometimes you want to actually compile and run and debug two different versionsof your product, and then the git model completely breaks down.
Even though it's ugly I'm sold on git but every time I recommend it to someone who is not used to it I tell them: "get used to the idea that you only ever have one version of your source on your PC, no concurrent branches in different directories." I know git half-heartedly supports multiple working trees but I think you're best off doing the "commit-WIP-then-branch" approach. Even stash is pretty ugly, I only use it for things like config files someone committed that I in turn would never commit (like IDE proj files, etc.)
I used to think the "one directory" for everything was the way to go. In a startup around 2001, I tried ten different VCS systems, including looking for ones I'd never heard of (e.g. AccuRev), and I initially vetoed perforce because it wanted to put different branches in different folders. When all was said and done, I selected perforce for precisely that reason.
But git goes further than simply not having "multiple checked out branches" as a concept. Instead, its conceptual model for why I have branches and what I want to do with them is fundamentally wrong, as this article points out.
This doesn't make any sense, at all, in any way or form to me. Everything from build systems to IDEs is tied to were the tree is, I most certainly _don't_ want to constantly change that.
> and when you switch branches we'll believe that you want to throw away what you were just working on.
Switching branches checks if something would be thrown away, and will not proceed if that is the case.
I am so sorry. That sounds awful. Life doesn't have to be that way.
- Checkout different branch
- Continue working
Into this:
- Checkout branch into new location
- (Close project in IDE)
- Browse to branch location, open IDE
- Move all terminal $PWDs to the new location, as needed
- Make a coffee while the IDE re-indexes everything
- (Compiled languages: Hit make, enjoy another coffee while the build runs)
- (npm and similarly structured projects: Hit install, enjoy another coffee while all dependencies are being fetched/checked/installed)
And I just don't see how that's desirable. With git I can create, merge and switch between branches in seconds, and don't really have any problems (I've almost never felt the need to have two branches checked out at the same time). But with tree-per-branch I only see problems, and all branch-related operations become slow and annoying.
One use case I could imagine where this is useful if you are simultaneously maintaining several mainline branches. But even within those "work areas" I'd rather be able to quickly switch branches.
What do you base this statement on? The only people I've ever seen do this is those who are firmly entrenched in how TFS or SVN work. That's a far cry from "anybody".
Isn’t this what `git commit -a` does? Or do I misunderstand?
You can usually infer exactly what I am working on just by reading the latest commit messages on all of my branches.
There is a lot of complexity around managing code that gets wrapped into the discussion about git. Everything from CI to code formatters to linters to audit tools. Most of these tools aren't VCS's. They are often language and toolchain specific or are some other devops lifecycle stuff that really isn't what git is doing. What git does do, it does wonderfully well - and makes it as easy to work with an entire codebase as it used to be to work on a single file. If there is a complaint about git, it's the thing many other comments hit - switching branches with staged to uncommitted changes requires some care... and removing history (e.g. someone commits a private key) is a little tricky.
I spent two years full-time working with a team that used Git. The work wasn't distributed; we all worked in the same room, doing the same hours. I never really got Git; too much unwanted complication, and I had quite enough to learn without having to learn Git at the same time.
I did like the ability to edit commits at commit-time, line-by-line. That was nice, but it's the kind of feature that could be back-patched onto something like SVN. It doesn't depend on any intrinsic design-element of Git.
I never got rebasing. It scared the shit out of me.
https://www.sevenmentor.com/ui-and-ux-and-web-development-tr...
I mostly took issue with GP's implication that there was no reason to perform a conceptual design review after the software's already created.
This is a critique of the conceptual designs, which is valid. And an improvement upon them IMHO.
I don't understand what you're arguing.