12 ms·
Certain systems can best be understood as black boxes. You put some commands in and magic happens. Git was not designed to be such a system and early users of g
by edejong 10y ago
Certain systems can best be understood as black boxes. You put some commands in and magic happens. Git was not designed to be such a system and early users of git know this.
During the last 5 years, many GUIs have filled in this gap, making it increasingly likely to find people completely stuck because they miss knowledge of the foundations.
Git is a utility to manage an append-only repository of tree-objects, blobs and commits. To help humans, git adds
- human-readable pointers (branches, HEAD, stash)
- an method to incrementally add changes (staging/index/working area)
- a method to append tree-objects, blobs and commits from repository to another
- some commands which alleviate steps in common tasks
These last set of commands cause pain, as users without foundational knowledge, do not realize these commands are compounding many small steps.
- godd2 10y agoIt's not just GUI usage that leads to bad understanding. The documentation is incorrect. man git-commit Record *changes* to the repository (emphasis mine) It's no wonder people get confused, if the docs do nothing to alleviate confusion. A commit is a snapshot of a directory, not a list of changes.
- antocv 10y agoWell, you are wrong the man page is correct. It is not a snapshot of a directory, if you want that use btrfs snapshot support, or use tar and gzip or duplicity. A commit records changes to a repository, which can be changes to only a file and not a whole directory of files, and the changes stored is only the delta of the previous change, and not "snapshot" in the sense of storing 2 different versions of your files in full - thats a waste of space, its stores the deltas, the patch - and other meta-data such as author and dates.
- derefr 10y agoNah, semantically, the parent commentor is closer to the truth. There are SCMs that literally store commits as patches or diffs, and then, when you want to check out anything other than HEAD, they have to run history backward by applying those patches, in series, to move through time. A git commit, on the other hand, is more like a handle to a pure-functional tree data structure that happens to share some of its pointers with earlier versions of the same tree. Each commit is still the whole tree—not a diff; any marginal commit just happens to not take up much space, because a lot of the objects within it are objects that were already entered into the pool in previous commits. You can easily see that this is true by cloning a large repo, using git's `git checkout --orphan` command to create a new entirely-disconnected branch, and then committing the whole repo to it. If git was diff-based, the size of the .git folder would balloon when you did this and your computer would chug computing the commit. But instead, this operation is approximately free, because your new commit will create a tree object that shares all its child objects with existing objects already in the pool, even though the "diff" of this commit is against a blank-slate state. (Mind you, git will chug if you ask it to `git show` this orphan commit; it will have to convert, right then, the (cheap) tree representation into a huge diff for you to look at. But that huge diff is just for your convenience; it has nothing to do with git's data model.)
- edejong 10y agoA commit in the repo is simpler than that. It is: - A reference to the hash of a tree object - Some meta-data (one or more parent commits, author, date, message). The commit command (git commit) basically takes the tree object which you created in the index, your HEAD pointer (the commit you were at right now) and some user entered information to create a commit object. So, notably, the commit does not store the changes! It stores a reference to the root of the index tree object (which contains references to the root of other tree objects or blobs). There are no diffs recorded anywhere, only calculated when needed.
- randomsearch 10y agoGiven how the above posters all seem to have a different opinion about the internals of git, the point of the article seems to be somewhat negated.
- randomsearch 10y agoGiven how the above posters all seem to have a different opinion about the internals of git, the point of the article seems to be somewhat negated.
- rajivm 10y agoActually, you are incorrect. A git commit is not a "patch" or "delta" as it may seem from the CLI/UI. A git commit consists of a reference to the previous commit and a reference to a git "tree" object. A git "tree" object is essentially a directory listing of sub-trees (folders) and blobs (files). Each blob and sub-tree is addressed by a hash of the respective object. So in a sense, each commit by virtue of the tree it references represents the entire state of the repository at that commit. You do not need to accumulate the diffs of each commit to find the repo. state at that commit. The way git saves space is two fold: 1) Since blobs are hashed (content-addressable), if the same file exists in two trees or in two locations in the repository it will not take up additional space. 2) Git occasionally re-packs the objects into packfiles that are compressed and leverage deltas to reduce storage space. This is the closest you get to storing deltas, but this is at the blob level and not the commit level, and is more an aspect of storage than how the Git data model actually works.
- godd2 10y agoA commit is not deltas. It contains a hash of a tree object, which in turn contains a list of hashes of blob and tree objects. The tree hashes are of course of tree objects which are your subdirectories, and the blob hashes are the hashes of your full files. When you make a change to a file, and then add it, git saves the new file as a full copy, and gives it its own sha1 hash. If you go into `.git/objects` and find the file whose name is the hash of your most recent commit, you can decompress it (zlib inflate) and the first few characters of the file will be something like "commit 485\0tree 6f3eeb2952a...". This tell us that this object is a commit object of size 485 bytes, and then after the null character is the commit itself. If you then take that tree hash and do the same thing, you'll see a list of blob hashes next to filenames, and tree hashes next to directory names (in a tree object, git stores the hashes as the raw bits, instead of ascii encoded, so if you want to follow the hash list, you'll have to convert the hashes to their ascii equivalent to find the appropriate object in the object store). You are correct that git uses deltas, but it doesn't use them for commits, it uses them when it recompresses your objects into a packfile (which happens when there are too many loose objects or when you pull and push). Every commit can reconstruct the state of your project at the time the commit was made. Each commit can do this without the help of any other commit.
- antocv 10y agoWow, that is informative, did not know. Thanks to you, and others!
- godd2 10y agoNo problem! :) Once I understood this, things like merging and shallow clones made a lot more sense. Although it makes rebasing more confusing. What's happening there is that git is creating patches on the fly, and then applying them to the new base, and creating new commits. But the resulting commits are snapshots of what the files would have been had you applied the patches yourself. Of course, there can still be "merge conflicts" since both branches might make changes to the same place in the same file. But since everything is a snapshot, if you have no pending changes in your working directory, hopping around the commit history is a safe action, so long as you have a branch pointing to where you left off.
- edejong 10y agoFirst line in the description of the 'git-commit (1)' man-page: Stores the current contents of the index in a new commit along with a log message from the user describing the changes. Since most people are searching the man-pages for a certain task (in this case, the task is to record the changes), the title makes sense. As a user, you are kindly requested to read at least the description part of the man-page.
- annnnd 10y ago> During the last 5 years, many GUIs have filled in this gap, making it increasingly likely to find people completely stuck because they miss knowledge of the foundations. Arguably, these GUIs became popular because Git by itself offers awful UX. I am all for obscure commands and switches (I use Linux and prefer CLI to mouse), but Git really took it to the next level. And it's not as if it couldn't be done better (as Mercurial shows). As a user I don't want to know the intrinsic details about how some system is implemented. I just don't care - give me an external model which helps me use it, and leave it at that. I have my own code I need to worry about. If most users get the "wrong" mental model when using Git, then the problem lies with Git, not with users. </rant>
- jahnu 10y agoGit is a master example of why leaky abstractions are bad.
- qznc 10y agoPeople complain about missing abstractions, not leaky ones. Mercurial has leaky abstractions imho. A Mercurial changeset ("commit" in git terminology) has a branch property. In git a commit does not belong to a certain branch. With both SCMs, most changesets/commits are on multiple branches, thus I consider the Mercurial abstraction leaky. Mercurial people seem to like this, but it just doesn't fit my mental model.
- Siecje 10y agoI think you are talking about named branches in Mercurial. You can use Mercurial without named branches and use bookmarks instead.
- marcinkuzminski 10y agoExactly, there is the flexibility. IF you need to track source of commits together with the permanent branch info you use Mercurial branches, if you want a "symlink branches" in GIT style, bookmarks are the way to go. It works nice with Pull requests when you can move branch pointer after rebase or updates of commits.
- skrebbel 10y ago> Certain systems can best be understood as black boxes. You put some commands in and magic happens. Git was not designed to be such a system and early users of git know this. This is true. A less favorable reading of the same story is "Certain systems are designed so well that they can be understood as black boxes. You put some commands in and magic happens. Git was not designed as well and instead requires that user to fully understand its internal workings or risk running into trouble". My theory is that people are so proud that they finally mastered git that they like to forget that it simply has a horrible UI (and by that i mean the CLI and the design of the core set of commands). I know of only one other popular productivity tool that has a similar sense of "you need to learn how it thinks, deep inside, or stuff will mess up" and that's Microsoft Word¹. I haven't seen shares of MS Word geeks loudly heralding its awesomeness here on HN yet. Maybe hackers just don't write documents? I can't come up with any other reason. ¹) Word works great once you learn how to use its styles (a bit like CSS classes), its fields, and its cross-reference system. MS Word's font and paragraph buttons are there in the toolbar to lure you to the dark side, or something. Don't use them! Friends don't let friends touch Word's font settings from anywhere but the styles editor. Like Git's, MS Word's UI is awful (awful) but its internals is like CSS-done-right for paper documents.
- akavel 10y agoI am totally aware of the internal model and exploiting it (and other features) daily as much as I can, and it absolutely doesn't change the fact, that I see the UX/UI as irregular, accidental, chaotic, and more grown than designed. A classical example of irregularity I'm constantly stumbling upon: `git remote remove` vs. `git tag -d` vs. `git rm`. Every now and then I type `git tag rm foobar` and have to break my flow to check if this did what I wanted or not, and then check manpages in angry frustration to find out what is the proper incantation to clean the resulting mess. Now, don't get me wrong: I love what I can do with git — the dance of rebases, add -p, splitting, merging, reverting, diff + vim + apply --recount, etc etc; and I'm now much more versed in git than in hg; but I never can forget that Mercurial's author did "miraculously" manage to make the UX and docs approachable, regular, easy and carefully designed for friendliness (...uhm, maybe except MQ?... :/)
- rounce 10y agoI'm surprised you get tripped up by `git rm`. How do you remove a file from your FS usually? In the case of `git remote`, `git remote remove` and `git remote rm` are synonymous. It's all on the man page.
- yoz-y 10y agoThe parent definitely did read the manpage if you read the post. The problem with git is that it has a different vocabulary for the same action (e.g.: remove) for different entities. (git push origin :branchName anyone?) By not breaking the compatibility the git interface accumulates cruft over time. I think they would gain by versioning the cli interface somehow and just removing old commands that were since replaced by better alternatives.
- nindalf 10y agoSomething must be wrong with me because I almost never remember the command to do that. Perhaps its because I only do it once in 2 months or so. Thankfully my browser has me covered. I type in "git delete" and it autocompletes to links describing deletion of branches and tags.
- ZeroGravitas 10y agoThis comment made me think back to a period of time just as git became popular when every second blog post was on the topic of "git is easy once you understand X" in which people who'd just had that moment of inspiration and discovered the innards of git would share their newfound knowledge. Inevitably, the comments would point out various mistakes and bad advice contained in the blog post. Often comments would then correct the authoritative sounding corrections in the other comments. It's almost like git hit some weird sweet spot where its UX was so confusing that it could confuse smart people into thinking they understood it, even when they didn't.
- mannykannot 10y agoIt has a Dunning-Kruger coefficient > 1, where Cdk is the number of successive aha moments before you really do get it.
- KKKKkkkk1 10y agoSome people are attracted to man-made complexity like flies to manure. See also: C++ and LaTeX.
- jk563 10y agoThis appears to have manifested itself in this thread too!
- collyw 10y agoThe problem is many of us don't want / don't have time to learn all the intricacies of gits model. Its a tool to help us with our jobs, not the focus of our jobs. Its also very powerful and as as a result most of us only need a fraction of its features.
- scalaJobs 10y agoSome of the commands have inconsistent behaviour regarding arguments. the classical example is the checkout command with/without a file as argument
- Ajedi32 10y agoI'm still waiting for a GUI that uses the fundamental model of how git works to its advantage instead of trying to paper over it. I saw a really great concept for this a while back, but AFAIK nobody ever really implemented it: http://tonsky.me/blog/reinventing-git-interface/ http://tonsky.me/blog/reinventing-git-interface/
- deleted 10y ago[deleted]
- pimlottc 10y agoFundamentally, git is a tool for manipulating directed graphs.
- edejong 10y agomanipulating directed acyclic graphs of directed acyclic graphs of blobs.
- restalis 10y ago"Certain systems can best be understood as black boxes. Git was not designed to be such a system" "In software development, a leaky abstraction is an abstraction that exposes details and limitations of its underlying implementation to its users that should ideally be hidden away. Leaky abstractions are considered problematic, since the purpose of abstractions is to manage complexity by concealing unnecessary details from the user." https://en.wikipedia.org/wiki/Leaky_abstraction https://en.wikipedia.org/wiki/Leaky_abstraction You may dress git's flaws as "design choices" or however else you like, but the king is naked to me! I believe the time will come when our descendants will look back at git only in a social research context.