Having taught git several times within a data science course I find two concepts especially worth extra time: WHY there is a staging area, and what is the difference between “git” and “github”.
I understand your second point, but I have a hard time understanding the difficulty with this part. Why is it hard for people to understand the idea of staging?
You put things in a box one at a time before closing the box. Does it require more explanation than that? What do people find difficult about it?
Because you not always want to put everything in the box (and if you do, there's a shortcut to do it), and "git commit file1 folder/folder/ * .cpp folder/folder/ * .h ..." for a complex set would be annoying and require you to mentally keep track of it from the beginning.
Many beginners will start by always doing "git commit -a" and that's fine, as long as they know there's an alternative once they need it.
Almost never actually. I never commit all the changes in my repo (for big projects I often have some small changes in other places, I don't want to commit them)
Not for me! I often find myself refactoring tangential features while producing a new one. Sometimes that will even intersect in a single file. But that refactoring doesn't come with any changes relevant to the feature I am working on in my branch. So I save them for their own isolated commit(s). While this doesn't happen on every commit, it probably happens for me about every other push. The alternative is bundling in a bunch of changes that have very little to do with the feature that my branch is ostensibly about.
EDIT: Now that I think about it, I also have several repos where I have changes that I never intend to ever commit them, because they are development conveniences for me personally.
Same for me! Webpack config changes to cache settings, config changes to hit a different API for testing, using a different database for testing. Most of these live in my staging area and get stashed and popped when I switch branches/rebase.
Not really. I think of my git use case at work pretty simple. I usually stash, pull down, fast-foward and then pop my stash on top. Occasionally I'll need to rebase too. Just to show I'm not a super advanced user or anything.
I'm a JS dev mainly working in React on a web app with a backend team using PHP. Often I'll be working on a branch with maybe 2 or 3 people and I often end up working on a few things at a time. Say I'm working on a feature, and I notice some bug I'll fix that and then get on with my feature. Once I go to commit I pretty much always do a 'git add . -p' and I very rarely want to add all the files I've worked on!
Even things like switching a config file to use a service like apiary where I don't want to commit my change to the config to use apiary.. Or change to my webpack config for testing, etc.
I've used Perforce, SVN and Git and the whole 'staging' area thing always felt very natural to me. Here are the files you've edited, which ones want to be commited? It gives me a second chance to go through and check everything before I've commited, and often that stops me leaving in any odd comments or debug code.
Not everyone stays a beginner forever, and it's nice to have a tool that doesn't play to the
lowest common denominator. It's really not that hard to just do a "git commit -a" if you
want to avoid staging.
99% of developers out there didn't need a power tool for source control (source control is already quite a power tool many devs can barely handle, even in SVN form...), yet here we are: Git is imposed everywhere, with its horrible UX.
Git's UX isn't that bad if you're only cloning projects to build them locally and keep them updated.
The UX only gets really crufty as you use more and more of the features.
Understanding the staging area first requires understanding the need for it: The need for atomic commits. The need to create commits that have specific changes in them and are not always a snapshot of the entire world below the git root exactly as is right now.
People are very used to the web "save always" style: There is one document, and you're editing it. Most people will be familiar with the traditional desktop "save" model where you have to do something to make your changes permanent.
People often then learn that there is a local file and some remote file: they can cope with a save -> upload workflow. Lots of traditional VCS turn this into a save -> commit workflow.
Git adds two stages to this that people can't see the need for without understanding the internals: an extra step between save and commit, and an extra step after commit.
(The discussion reminds me of all those people who think that if they just start by talking about monads then people will find Haskell easy and natural...)
The don't need to understand the internals for this: just knowing that every save you do will be stored forever as-is makes you double-think about what you put inside
So I have a solid mental of git, and I understand the theoretical need for the staging area.
However, I find the occasions for using the staging area in practice are few and far between, for the simple reason that I can't test and execute the code that's in the staging area without also having the code from the working directory also be there. It feels like after having partially staged some of my working directory, it would be a blind commit with no guarantee that things are working.
Very rare is the situation that I can break out a list of files over here that are for feature A and some over there for feature B, and never the two shall interact.
I think this is probably what most struggle with regarding the staging area, without being able to articulate it.
This has never made sense to me. I've seen others say that they commit only parts of a file. How does this scenario start? Are you working on solving one problem, but then notice some other unrelated issue and fix that too, before committing the first change?
Partly, yes. Or, I'll be working on a task overall, and have to touch multiple files in the process. Then when I'm ready to commit, I review all the modified files on disk, and look for ways to break those down into smaller discrete logical changes. I prefer to avoid "big bang" commits as much as possible, because smaller individual commits are easier to inspect, easier to back out if necessary, and provide a better "story" when inspecting a file's history sometime down the road.
But then, you either never run/tested those smaller individual commits, or you have to do extra work (stash changes, test, restore stash) to do that.
I do not see why a source control system should make it easier to make a commit that hasn’t ever existed on disk and thus cannot have been tested.
I think the better model would be to stash your changes and have an diff editor between the on-disk working copy and the stashed version that allows you to commit a set of changes as several smaller, more coherent commits.
That wouldn’t guarantee that each of those intermediate commits gets tested or even built, but it would guarantee that each smaller commit is in the on-disk copy at some time.
> But then, you either never run/tested those smaller individual commits
Not necessarily. One nice option that the git rebase command has is --exec (which can be specified multiple times). So you can run a rebase and have git execute a command (like running a test suite) for each commit in the branch. If any commit files, the rebase process will stop and let you amend the commit to fix the issue.
> or you have to do extra work (stash changes, test, restore stash) to do that.
I've found that it's easier to write and locally test a given feature and them incrementally stage parts of it and create commits before pushing the code up for review. To me, that's easier than just making a large commit and then trying to split it out into a better set of commits after the fact.
For example, I may write a new method and then call it several places in the code. So my first commit would be to add the new method along with its unit tests and my second commit would be to add calls to it in the code base and update the associated integration tests (if necessary).
One common scenario is that I'm working on one problem, and in the process of solving that issue do some refactoring of related code. In this case, I want to commit the refactoring (which does not change the program's behaviour) before committing the changes that do change the program's behaviour.
I typically then send that first refactoring commit to Github (on its own branch) so that it gets full CI test coverage. And then continue working on the fix/feature while it runs.
> Are you working on solving one problem, but then notice some other unrelated issue and fix that too, before committing the first change?
Almost. Most often it's:
- Working on solving problem A
- Notice problem B
- Start to solve problem B
- Notice I'm getting distracted from A, and return to finish it.
- Want to commit my fix for A, but don't want to lose or forget the partial work on B.
Two different approaches I might take in this situation, depending on whether B is related to A.
1. If they are related (eg, B depends on A), use `add --patch` to commit A, then finish and commit B.
2. If unrelated, use `git stash --patch` to stash B, then commit A, then switch to a different branch to finish B.
Honestly, I see the point of both stash and staging, but not both together. Too many tools for the same job. On my long list of projects to do is a git porcelain that combines some of these concepts (eg, stash and working directory which would be tied to a branch):
- Each branch would have a single stash.
- When you check out a new branch, all uncommitted changes are automatically stashed.
- If the branch you're switching to has anything stashed, that stash gets popped.
- Any current workflow that involves stashing can be replicated by using a branch instead of a stash.
This way, branches can be thought of as "state of the working directory", which is more intuitive with the branching tree model, imo; commits are a snapshot of the repo at that point in time; and the staging area is just a way to choose what should be included in those commits.
One use case is to exclude extra lines of the file you don't want to commit. For example, I might have some debug print statements in my file that I want to keep in my local copy of the file while testing, but I don't want to include in the commit I push up for review.
I second this. It wasn't until I adopted this practice that the staging area really made sense to me. I find it helpful not just for making atomic commits, but as a way of remembering what I was actually doing, so that I can write a good commit statement.
> Git’s workflow wouldn’t even be sane without the staging area. This is what allows you to fix mistakes and make your work presentable for remotes.
I did exactly the same diff/tidy/diff workflow when I used p4 and svn, neither of which make a distinction between "working directory" and "staging area".
Right, but p4 & svn have “checkout” which is similar to staging. Staging is part of what we get because we can edit files without having to checkout / open for edit.
P4 and svn don’t have a strict commit parentage, which is why you can push commits in those systems in any order. Git’s strict concept of parentage is what makes the staging area so important for keeping your workflow similar to p4 & svn Workflows. Without a staging area, you’d either have to always fix mistakes with new commits, which is bad, or rewrite already pushed history, which is worse.
The terminology is a bit different - unless configured with mandatory locking (essential for some workflows) you don't have to open for edit. You just edit stuff and it goes in the "default changelist", roughly equivalent to automatic staging.
> Without a staging area, you’d either have to always fix mistakes with new commits
Mistakes at what point? In the normal svn workflow you can review with svn diff, then when you're happy do svn commit; it's just that there's no local place you're committing to. In both cases there's a critical point, either "svn commit" or "git push".
> unless configured with mandatory locking ... you don’t have to open for edit.
I’d guess you’re learning toward talking about svn, which I don’t remember very well, and I am leaning towward talking about p4, which always does mandatory locking.
You’re right the terminology is different between these different systems, I’m just pointing out that the git staging area has what you can think of as some equivalences in the other systems. Or, you can think of it as tradeoffs. Either way, the git staging area is something that helps you pretend like you’re using svn or p4 in the sense that it helps support editing multiple changes at the same time before pushing them to a server.
> Mistakes at what point?
With git I’m referring to mistakes between commit and push. But there’s a philosophical difference here that I glossed over. With git it’s easier to commit early and often than it is with svn or p4. With svn & p4 it’s easier to lose your work because version control doesn’t know anything about it before you push. If I make micro-commits, which I want and I like, then I put more “mistakes” along the way into my local history, and I can use the staging area to clean everything up before I push. With svn & p4, you make those mistakes and do the cleanup without ever telling the version control, and you run a greater risk of losing that work while you do the cleanup.
You're adding a feature to your proggie. That involves modifying the main bits to add the feature and, say, adding a couple of interfaces to internal library modules.
Split out the changes to the library modules into separate commits---it's safe because nothing uses them, they're logically separate from the feature changes (although they don't appear to have a justification without the feature), the log will be marginally cleaner, and git bisect will have more granularity.
Why is the staging area needed in such a case ? In more traditional systems, you'd just do, say, "svn commit library/" and then commit the rest. (and you could do just the same in git too without seeing the staging area)
Committing isn't a commitment. After making the first commit, you can use the `git stash` command to put the rest of your changes aside, and go through the normal test->amend loop until you're happy with that first commit. Then you just retrieve your other changes from the stash to make your second commit.
It's also possible to do this without the stash command, by making both commits right away, and testing them later. However, that would involve rebasing(?) your second commit on top of any changes you end up making to your first commit, so using the stash makes more sense to me personally.
Fwiw, stash can get you into trouble more easily than commit. It’s no more typing to commit or branch, so I recommend preferring those to stash when it makes sense, or when you’re playing with changes you don’t want to lose. Stash is handy for a bunch of things, so use it by all means, just remember that there’s often an equivalent way that is just as easy and much safer.
“If you mistakenly drop or clear stash entries, they cannot be recovered through the normal safety mechanisms.”
One of the best things about git is how big the safety net is, as long as you tell git about your changes. Almost any mistake can be fixed, so why use features that aren’t sitting over the safety net?
I think people find it difficult because for most beginners at git, they just want to put everything in the box. Having the option to put just some things in the box seems more complicated than needed. Obviously, as you get better with the tool, you realize the power of literally "staging" your changes into multiple commits, but as beginner, it's not even in your purview.
Yes, it requires more explanation than that. I've used git for years, and never really understood why staging is even a thing.
Your example is an implementation of the box-putting algorithm, but it doesn't need to be mirrored in the put-box CLI.
put-close-box file1 file2
This command could encompass all the putting and closing. Since you only close boxes when you are done putting things in it, I don't see a need or purpose to split it up.
put-box file1 file2
close-box
A closed box (commit) is always going to contain stuff that was put in it, so why separate commands?
That's not convenient when you're putting things into the box piecemeal, especially with `git add -p`. A thing I do frequently is to run `git diff`, scan through it, and add files (or parts of files) one by one in a second terminal. Then I do a final review of the staging area (with `git diff --cached`) to make sure it only has the changes I want and commit. I'm the sole devops engineer at my company and my workflow is a bit more scattered than a typical developer's.
Anyway, `git commit file1 file2` by itself is most of the way to being the put-close-box function you want; it just doesn't work for adding/deleting files from the repo. Seems like they could make a lot of people happy by closing that gap and letting `git add` be an intermediate-level feature.
To me, that ought to be a concern of the "porcelain", although no one uses that word anymore. CLI is particularly bad at certain types of interaction. So to compensate, a mitigation is moved into the underlying model of git. That mitigation is staging. The inconvenience of "piecemeal adding" could have easily been addressed in the UI layer using a more suitable presentation, rather than forcing all clients to follow the stage/commit dichotomy.
The staging area is really an extraneous concept that isn't required. It's like a commit that isn't a commit.
In Mercurial, I much prefer to just make it an actual commit in the draft phase (the default phase) and just keep rewriting that commit. Mercurial provides tools for both selectively adding and removing hunks from a commit (both `hg amend` and `hg uncommit` accept --interactive for hunk selection). If you're extra paranoid, you can make it a commit in the secret phase so it's not shared prematurely by accident.
It's pretty much functionally equivalent and doesn't require an extra location in which your code can be. It's either in your working directory or in a commit.
A bonus of this approach is that now you have a meta-history, hidden by default, of what you've "staged" and "unstaged". It's kind of like a reflog but with, in my opinion, a better UI. And of course, the index/cache/staging area in git doesn't use refs, so there's no reflog there.
I've helped move a couple teams (kicking and screaming) from TFS to git, and I start back even further than that - why is it so much more complicated than clicking a button to save and share my work, and what is the benefit of that complication?
If I had trouble with understanding the reason behind add->commit->push workflow, I would definitely have no idea what this article talks about when it says things like "merge, rebase, diamond shape". The flow chart looks almost exactly the same for "pull" and "pull --rebase". The only difference between the charts is the wording which has no meaning at all for a newbie.
> The flow chart looks almost exactly the same for "pull" and "pull --rebase".
"pull" and "pull --rebase" can cause two kind of conflicts:
1. Merge or rebase conflict inside the repository.
2. The would-be merged or rebased HEAD conflicting with the dirty working directory.
The article demonstrates the latter and it's the less important one as it's avoidable by pulling from a clean working directory or pulling a non-HEAD branch.
It sounds stupid but something that really simplified git to me as an absolute beginner about a year ago was the fact that it doesn't exist on GitHub. Knowing that git was just something that sat in the folder on your filesystem and monitored changes took away the notion that there is some kind of sync between your remote and local. Working just on my own laptop made me realise how intuitive all the commands and strategies for solving problems were. Then came pushing, pulling etc between branches and it all just fell into place.
If so, I'd love to see a more elaborate, guide-like version of this model.
I've tried a similar approach years ago for teaching and failed spectacularly. Eventually, my peers became comfortable when they got used to the Github Desktop Client. They compared the buttons they click with my terminal commands. We also compared our graph views on Github website to visualize the logic.
It's been years and still none of them used rebase even a single time. A sad story in my teaching non-career. :(
Its opposite for me. I am very comfortable with the command line. It lets me do crazy things and if I mess up, quitely crawl back to the peaceful place.
GUI integrations have some advantages but the knowledge is not transferrable. Different GUIs work differently but the commands stay the same. I think both can complement each other. I use Pycharm a lot and it is a lot easier to see diffs or file history there. I think the same can be done with cmd as well but I don’t think I ever bothered to learn advanced commands.
I like the commandline as well, but most of my coworkers are happy using a git GUI, and I can see why:
1. Most GUI's give you an overview of the changes before committing.
2. Most GUI's let you commit and push in one go, and also show unstaged changes so you don't forget to add/commit/push anything.
3. A good git GUI is explorative. Newbies just remember the icons to click at first, and they learn more by exploring menus and reading messages.
4. Commit histories are easier to view and filter.
5. Exotic steps are easier to do, since you don't have to remember commands you barely use.
6. Adding remotes is a piece of cake.
> and if I mess up, quitely crawl back to the peaceful place.
Ok, but getting to THAT level of comfort takes a long time and many failed attempts.
While I understand why folks may want a GUI for git, I am continually amazed that there hasn't been an effort to "refactor" git commands so they're more consistent, easier to remember and easier to discover. It's a miracle that git has taken root so strongly, given it's shitty user experience.
I've been using git for years, and STILL, I need my cheatsheets and google far more than I would ever admit in person. Anything that's outside of heavily practiced workflow-- and I'm in a world of confusion.
Committing to version control is one of the ideal uses of a GUI. You can skim your eye over the files you've changed, and flick between their diffs by just clicking on their file names, before going ahead with the commit. You can simultaneously cast your eye over recent commits in the revision history. Of course these are easily accesible with git status and git log (|less) but it's not the same as dealing with the information graphically.
I would posit the ed text editor for comparison. Why bother with a graphical text editor (including terminal editors like Vi and Emacs)? After all, you can look at the context surrounding lines you wish to edit by entering the appropriate ed commands.
I think it comes down to developers being so used to working with command line tools that they don't give GUIs a proper chance - ironically the exact objection they have with less experienced users not using the command line.
The problem is that most Git GUIs are just that—Git GUIs, with much of their functionality being a thin layer over a subset of the decidedly poor-usability Git command line interface. There are small areas of their functionality where I find that some do a really good job, but mostly they still require you to learn the underlying concepts and nuances of Git rather than of version control in general. I am not aware of any that have evidently been constructed from the other end, focusing on the users and workflows, bending Git into shape around that. Naturally, there are dangers of being opinionated like that when it comes to playing ball with other users that might not use that particular client; this is a hard problem, which explains the paucity of attempts. (Making “a Git client” is easy; making a good tool for users is hard. It’s similar in other domains where there’s an easy path and a far better path—the far better path is seldom trodden.)
Not sure whether your statement is ignorant or insightful.
Yes, once electric cars (with superior traction and brake control) take over automatic gearboxes (actually electrics don't even have gearboxes) will be the norm, but as long as there are engines driven on dinosaur fuel there will be a need for manual gearboxes. Which is why you should learn how to drive one. Once you're able to do that, driving an automatic is trivial, which was the point I was trying to illustrate.
> In the U.S. today, less than 2% of new cars sold are manual now. Automatic gearboxes are well beyond the norm already.
At that percentage, the U.S. is a huge outlier however, and accounts for quite some chunk of of worldwide automatic gearbox equipped vehicle production on it's own.
The automatic gearbox remains the less popular option worldwide [1], though as you can see they are more popular than they were before.
The real reason that's the case is because most of the rest of the world is much poorer than the US. Also many the people in those other countries don't have to drive as much or as far as people in the US.
At some point it's just Stockholm syndrome (we can only afford manual, everyone has it, so let's at least pretend it's cool!).
Same here. I am using Git for over 10 years now (with breaks), but I always pain me getting into the CLI (apart from basic add/commit/push/pull). Almost all commands require flags to give you a decent behaviour and the documentation is seriously lacking.
For example, I've never had any problems with Mercurial. Everything just works there and it is intuitive. You can never lose data there, and it has extremely sensible defaults.
> Using a git GUI works quite well for people inexperienced with git.
I agree, and I wish there was a really good git GUI that either abstracted git nicely, or was as powerful as the CLI, or both. I’ve tried many of them, in production, and there aren’t any that give you a UI for everything git can do.
The problem I’ve noticed is that people raised on the GUI often don’t understand how to get out of trouble once they have a serious problem. Also the GUI tends to be a crutch that prevents them from learning the CLI well.
Git is natively a CLI and it really shows once you know both. Git’s CLI interface is awkward and hard to learn, and all the GUIs for git are somewhat awkward, both due to git being awkward, and also because trying to fit UI workflow onto CLI commands introduces awkwardness. All git GUIs are incomplete interfaces to git, there are none that give you access to all of git... specifically things like finding lost data and managing repos is something you’ll need to drop to the CLI for.
Some ideas on how to teach it non-verbally in class, regarding the add, commit, push workflow:
In class I'd stand on my right side, so students see me on the left (people are more used to left to right motions because of reading).
I'd demarcate the working directory, staging area and local repository non-verbally as 3 different spaces. My most right space (the student's left) was the working directory, my most right space (the student's right) was the local repository.
Everytime I'd make a transition from one space to another through add or commit, I'd make a hand gesture when I'd say "add" or "commit".
In order to add some humor (humor is rememerability) with push I'd point upwards to the ceiling as if I were looking to God. I'm not religious, but people got the reference and they all left out loud.
I think the best thing you could do for novices is to avoid priming them with preconceptions of Git being difficult. Psyching people up before instructing them never seems to do people any good, yet it's very common.
I agree with this. For many people who are learning Git for the first time, it's also their first experience with contributing code to a repository at all.
The last thing you want to do is turn them off entirely, or gatekeep the profession to exclude people who aren't good at reading dense documentation.
The best way to teach git is to get them comfortable with "Add, Commit, and Push" and then explain what's happening at each stage.
Despite using CVS and SVN for years, I was never truly comfortable with either. When I first learned git, it was like a breath of fresh air. Suddenly the VCS was behaving in predictable ways. Yet I'd put off learning git for two or three years because everybody was telling me "git is so much more confusing than subversion." I shouldn't have listened to them, and they shouldn't have said that!
Maybe if somebody has already mastered subversion then git will confuse them a lot, but I'm not convinced even that is necessarily true. Regardless, it seems clear to me that novice users shouldn't be told that git is complicated.
Here is my personal recommendation for getting more comfortable with git. Use "git status" a lot. Everytime you do something in git, and before you do something, do a "git status" and see what you change with your commands. And what you didn't change.
`git log` on its own (with no flags) isn't that useful, as it's missing a lot of important information. I prefer `git config --global alias.lg "log --color --graph --oneline --decorate"`. Then you can just type `git lg` and get a much more useful overview of the state of your current branch.
for easy copy-pasting. HN doesn't support backticks or triple backticks like a lot of markdown parsers. To get fixed-width fonts, you have to insert 4 paces at the beginning of the 'code' block.
I don't think it was ever really intended for worldwide use by Linus, it was intended to exactly replicate his workflow and his alone. The real responsible party is github; I'm not sure how they came to dominate?
By being much, much, much better than the competition at the time (sourceforge) - github was faster, without ads, used superior vcs. Github also had Octopuss/Octocat as a logo :-) .
Yes, the gui was good (and it still is, or may be I am too familiar with it at this point, I do not know).
Another factor is that git was always FAST, and the most common operations (init, log, status, commit, checkout, push, pull) are not that complicated as people make them to be, so you can very easily start to use it for new local projects, and continue without installing additional plugins, unlike mercurial ... And at some point, when you wanted a colaboration hub or just a public remote repo, you just pushed your code to github.
This makes a lot more sense when you realise that Linus thought he was building a low level tool that people would build a UI on top of. The 'original' git command line was more a proof of concept and engineering tool than something aimed at actual use.
Right, doing a search through the git mailing list for the use of the word "porcelain" is fascinating. It's unfortunate some of those "porcelain" projects never finished.
Heh. I think Linux on desktop it is like that, at least a bit; frequently, by the time someone could write a friendly GUI (or friendly CLI for that matter), they have little use for it.
It definitely feels like it was written by someone used to writing an ABI or library rather than a set of command line tools.
Certain tools are organized by what code goes together rather than by what user functions make sense to go together. It's part of what makes it confusing.
What an absurd statement: "Torvalds has a proper excuse for the pain and suffering he's inflicted on millions of developers worldwide :(" !
Who are you exactly to judge people like that, oh failed incarnation of a tibetan Lama ???
He does not need an excuse to make a tool that has proven very useful to many (including me). In fact, I am grateful, that he chose to make it and publish it.
> Git [...] feels like the designer [...] said "F*ck this shit, make your own UI on top if you want to use this tool!"
Actually this is exactly what happened back then, which is why many people used https://en.wikipedia.org/wiki/Cogito_(software) as a frontend to git in the early days. That said, I personally find current git UX absolutely fine and I can't imagine being effective without having all those commands like git reset --whatever -p and git rebase --interactive.
I got into the habit of teaching people `gitk`. It's not the prettiest tool, but it's included with most distributions of git, defaults to a decorated color graph log, and thus avoids the "copy paste this weird config line" step on other people's machines.
I just use a GUI for that. I don't like using applications like Sourcetree for any actions, but they really are superior for visualisation of the commit tree, diffs between non-adjacent commits or between branches, etc.
I'd recommend Git Extensions, still, as a good compromise between porcelain and plumbing. The most useful commands are all at your fingertips, with deeper ones available if necessary. Visualization is best I've seen - clean graph display, easy to read, and _doesn't lie in complex cases like SourceTree does._
SourceTree tends to treat commits with multiple ancestry in a very weird way that has led to difficulty on multiple occasions, where people think changes 'went missing' because SourceTree decided the other ancestor wasn't important enough to show.
I don't use Windows. Seems a little clunky to get it to work on Linux, even more so on the Mac. Haven't had any multiple-ancestry issues with SourceTree - perhaps resolved in an update since you encountered it?
`git log -p` is my favorite obscure git command.
Shows you commit by commit changes. If you specify a path, it limits to only those files. If you do a single file you can do `git log -p --follow <file_path>` and it will track the file across moves and renames.
Also `git whatchanged` is a super helpful command to see just the list of files that changed in each commit
I got mixed results with a completely different approach: starting with what actually exists within a Git repository (i.e. roughly: focusing on aspects of the plumbing layer first).
However, this only works with people who can make the mental leap to be able to deduce knowledge of what should be from knowledge about what is. In the end, I concluded it's a bit like teaching cooking. There are those folks who need to be taught about full recipes and those who need to be taught about resources and corresponding steps.
I have not yet seen a working approach to make both of these fractions happy, unfortunately.
I once taught[1] the building blocks of git (basically bullet time view of a commit) and people found it a bit too theoretical - even though it contained all the elements, that helped me to understand and appreciate the simplicity.
There is a point, where you go from memorization (add, push, commit) to deduction (graph, objects and refs) but when this point is reached, depends on many individual factors.
When I was first learning git, I found an online visualizer like this [0] that really helped make make concrete the ideas of git history being a graph, and what different operations did on that graph.
There was still obviously the issue of memorizing the commands, but at least I knew what the commands were doing on a deeper level.
The other problem I find in git is that there are many GUI interfaces and none of them are consistent. In Eclipse I had a different interface depending on what project I opened, despite both the projects being in Python.
I found this very helpful: http://eagain.net/articles/git-for-computer-scientists/. Git's data structures are well designed. Once you have internalized them, you will be better equipped to navigate through the jungle of command-line options.
To understand the data structures interactively, use "git cat-file -p HEAD" and continue drilling down to an individual file in a subdirectory with "git cat-file -p OBJECTHASH"
I've never had to understand the internals of a web browser or text editor in order to use it; drivers ed courses don't start with a discussion of thermodynamics. Why should it be necessary for git?
Because every source management tool has a model, and to use it at all you need to know the model. Else you're jabbing buttons and turning dials on a complex machine and the outcome is going to be tragic.
I've used SVN reasonably well without knowing its internal model. I kind of knew a bit about it, I'd never call myself an SVN expert and I still managed to do my job efficiently.
> It gives you control to manipulate the repository like say, a relational database
And how many people do that? Not that many. This angle is a bit like the guy who said that he doesn't want Unix file names to be UTF-8 text-only, because he crafted a sort of relational database on top of a Unix FS and by having file names be just text he couldn't do some super niche trickery. I think it was in response to this article: https://dwheeler.com/essays/fixing-unix-linux-filenames.html
> If svn is enough for you, that's fine.
It is, but every job these days forces you to use git. And most places I've worked at, git is used as a glorified SVN where people just have a 2-step commit to a remote server.
I regularly fix my commits, (just like I edit most comments on hn within a few minutes after sending them). And sometimes I take back commits even from the server (working in small teams).
Locally, I regularly switch to older commits and branches to try things out. In svn all this requires a network connection and it is quite costly. Which means you do a hell of a lot less of it. And I would assume it shows in software quality.
Man, how I used to freak out regularly waiting for a stupid svn diff or svn log to finish. These operations are the bread and butter of version control, and they are instantaneous in git.
Or, aren't you regularly blocked from doing quick fixes using svn for problems that you saw, but couldn't fix them because you had a different pending commit? I have that often when working with svn (admittedly I don't know svn very well, but I'm sure it is a real blocker in many situations). Situations like these are easy in git.
And how idiotic, after all, is it that we have to setup a SERVER to keep a simple log of changes to a few files? I have so many small ephemeral projects that I simply put in my laptop's home directory. There is no point in maintaining server repositories for those. Git is simply a tool that lets you do that. It lets you do bookkeeping of your data. It's a tool. While svn feels like an inflexible process. (yes, svn has the file:// protocol, but you still have to setup a separate repository, right?)
In a sense, git is the sqlite of version control. You are really missing out.
> It is, but every job these days forces you to use git.
Here in Germany, svn seems to be the norm still in the engineering domain. But I figure the situation is quite different for web shops.
> And most places I've worked at, git is used as a glorified SVN where people just have a 2-step commit to a remote server.
That's my experience as well. When it's about communicating changes most setups will be centralized just like a regular svn setup. It's a fine approach for small teams. But with git you still have the huge advantage of being independent of the server. And you can easily fix problems that are already committed to the server.
No the logic is not circular, and the advice to learn git is a good one.
It may seem paradoxical at first to you, but is true (as are many things in this profession). Another paradoxical advice like that, is to learn vim, or emacs, but I digress.
Git does not suck - as any other tool it, it just has strenghts and weaknesses (for example working with very large binary assets is its main weakness).
The UX of most common git CLI operations is clean actually, as they are fast, and you do not need many arcane options (although they are there, and are documented well, for people who read...).
If you screw up something, you just use the reflog to fix the state of your repo in most cases. Even if you can not (or do not want to), the troubleshooting is still easy - you can always do a fresh clone from your remote repository in a new folder and copy what you want there.
You're assuming that I haven't read about Git. I've read a ton about it and its internal data structures. And regarding your digression, I'm a vim user.
Regarding Git, Git does suck. It does the job Linus designed it to do, but that job is not most software engineers across the world need it to do.
In smaller or in corporate shops, Subversion was almost adequate and several bad implementation details, mostly related to branching, led to its demise. So that world needed Subversion++, not Git.
In the FAANG world, there's basically no company that uses Git as-is. It's strength/weaknesses aka tradeoffs aren't good enough for them.
Git won because tech is a popularity contest and people in our domain like to do a lot of virtue signalling ("this tool is hard to use, I use it, so I'm special/cool").
Git won because it is technically superior (IMHO, but I've used most other VCS only sparingly). It's well designed, flexible, and fast. Apart from a command-line interface with some unfortunately named options and a "big file issue" (that has never been a problem for me), I don't think there is anything wrong with it.
What do you think is wrong about it? Or have you only "read a ton" but never used it for a while? In the latter case, I suggest you start with the things I mentioned above, and make good use of "git reflog" as suggested by somebody else. If you know git reflog, "delete tree and clone a fresh copy" is not a thing anymore.
> In the FAANG world, there's basically no company that uses Git as-is. It's strength/weaknesses aka tradeoffs aren't good enough for them.
My response was to your sarcastic mini dialog above my reply. Do not try to read other peoples minds - it is impossible, and if you really want to, you can simply ask.
>> It does the job Linus designed it to do, but that job is not most software engineers across the world need it to do.
Speak for yourself, you are not most software engineeers.
>>In smaller or in corporate shops, Subversion was almost adequate ...
I have administered SVN profesionally for several years (2005-2007), and was paid to unfuck screwups made by other developers using it (which many times involved restoring from incremental hourly backups done on the SVN server side). Dealing and helping others with their git problems is many times easier.
The FAANG world (which I had to google just now) I imagine has unique requirements (many teams that must coordinate, super gigantic legacy source code base), and the resources to do whatever they want (money, humans to develop and maintain tools and do research). For them, the integration pain from managing multiple smaller repos may be significant. Outside this world however, teams are more independant and the source code size is much much smaller (even for legacy projects).
>> Git won because tech is a popularity contest
You have a point here, but this factor (and network effects in general) is just inertia, and does not explain why git won, given that for example SVN or Perforce had such a head start (in tooling, and in mindshare), and there were other distributed contenders like mercurial and darcs and BitKeeper developed at aproximately the same time, and even earlier. It won in my opinion because it was simply superior tech - faster, good enough and very very easy to get started.
You don't need to understand git's internals; I for sure don't know how it does delta compression of pack files etc. to provide you with efficient storage of snapshots and whatever else it does.
However, what you do need to understand is the model that git uses, which is extremely simple.
Git provides you a way to store snapshots of data into an on-disk graph data structure* that you can sync to and from remote repositories. You also get refs to store symbolic references to the snapshots in the data structure, and you can sync those too.
That's pretty much my mental model of git in its entirety, and it allows me to merge, branch, rebase and perform all kinds of commit surgery with ease, because I can always tell what effect an operation is going to have on my data structure (and even if I'm wrong, I can't lose data)
I seriously can't see how it could get any simpler.
(*) a git repository can actually contain several separate graph data structures, but that's usually not what you want...
I disagree with this idea. The best way to learn git is to read the git book, in this order: chapters 1, 10, 2, 3, and the rest at your discretion. This way teaches you about the internals first, and if you understand the internals the rest of git is pretty intuitive.
That's simply not true for many Git users. I'm a developer, so I do use Git every day, but I work with a bunch of researchers that absolutely do not need to use Git more than once a week at most. Convincing those people to care enough to learn the internals has been a constant uphill battle for me.
How about now :) Rust and C++ are complicated languages, and that's bad.
But git isn't complicated. Git is a handful of simple ideas composed in interesting ways. It looks complicated because there's a lot of porcelain commands with a lot of options, but all of them are just manipulating the same simple internals which, once understood, are clear and intuitive.
The difference being that you will likely spend hours of your day thinking in rust, while git should be taking minutes of your day, but often ends up taking hours when you screw up a command and need to restore things to how they were.
There's something to be said about reading documentation rather than relying on stackoverflow answers or possibly inaccurate tutorials.
Substitute C, Java, Python, etc for git. You can probably do something with those languages, but you aren't going to get very far without reading some sort of documentation.
The article is about how to get a more fundamental understanding idea on how git works, and this book demonstrates fundamental ideas on how git works. I don't see a problem in this reccomendation. If you want just a cursory knowledge of how to use git to get by, this probably not the right choice, but that's not really what this discussion is about is it?
Unfortunately, it isn't possible to effectively use git without knowing something about the internals. You can do the basics taught more 'by rote', but sooner or later you're going to run into something unexpected, or something complex you need to do and you need to understand the data model in order to have a chance of sorting it out.
The best way to learn git is to learn what's happening at the DAG level. That way you can think about what should happen on the DAG and then think of how you can use git to achieve that. For example, a fast-forward merge and a reset can be used to achieve the same thing.
It's also very important to learn to use the reflog. When I was learning to climb they told me I'd never get really good until I'd fallen once. The same thing goes for git. People are really scared of it because they think they could lose work or something. Thanks to the reflog and the way git works, that's actually quite difficult to do.
I learned that Git stores data as DAG at the very beginning when introduced to it. But I came to get clicked until I realized that it is not only a DAG but an immutable one. That is, existing nodes of the DAG are never changed once created. The only operation supported by the system is more or less: create. Also, using the plumbings to peek into the content of the objects and refs inside the .git helps a lot as well.
Yeah, that's a very important point. Even changing the parent of a commit, that is, an edge in the graph, changes the hash of the commit. Therefore to change the parent (like in a rebase) you have to make a new commit, but the old commit doesn't go anywhere, you just can't see it because you have no reference to it any more.
Git will garbage collect these commits that can't be reached by any reference after a while, but usually that's long after you've forgotten they ever existed.
Yeah this would be great. I started working on adding support for <hook>.d/* in addition to <hook> a couple of years ago, that got to a proof-of-concept stage, but some of the long tail is hard to handle so I dropped it, but it could be finished.
There was also a disagreement about what the semantics should be, e.g pre-receive.d/* hooks failing on the first one that failed in glob() order or not, which has implications for whether they can run in parallel.
The bit that stuck from those patches was being able to configure the the core.hooksPath variable to a hook path, which sometimes can with some stretching fulfill some of the use-cases, but most users end up with a meta-hook runner of some sort.
The glob() function is sorted consistently on all platforms. It's mandated by POSIX[1], and from the OSX docs I could find[2] sorted unless you specify the POSIX GLOB_NOSORT flag, as on other platforms.
In any case, even if glob() wasn't portable it's easy to provide an API compatibility layer that's just readdir() + qsort(). That part would be trivial, and might perhaps be needed due to collation issues in the sort.
Doing it in parallel (preferably in a random order) isn't just an optimization, but would ensure that there isn't an implicit dependency on whatever iterative interface we'd first ship with.
For anyone looking for resources on teaching git I'd recommend the software carpentries - all open source with a great community behind it. http://swcarpentry.github.io/git-novice/
Years ago, I tried to teach git to my (college) students, and it was a complete disaster. Like a lot of technical things, it's easy to get them totally confused with a single off-the-cuff sentence (and to be honest, I think I underestimated how difficult it would be to explain it and the kinds of pain points they'd encounter).
Reading this article, it occurs to me how useful the idea of a "staging area" would be to helping them understand. I don't think of it that way myself when I'm working with it (I suppose I do, but not in those precise terms). But looking back, that's what was tripping them up. If you're just talking about local and remote repositories, you're not really giving them the right idea of the workflow.
Git itself is so simple, it's all the stuff around it that can be overwhelming. Social mores about all the possible workflows are maybe the biggest (rebasing vs merging, granularity of branches and their longevity, acceptance of partial commits reflecting a state never realized in isolation on disk, commit hooks, requirement of every commit to satisfy properties x,y,z, direct access to common parent repo copy vs requiring some sort of pull request flow (and dependence on github and all their stuff)) but there's also work tracking, code review, build automation, test automation, deploy automation...
No one is appreciating the effort and a different take here. Let me congratulate the author on job well done.
There are people who love illustrated explanation and for those these are perfect. This is just meant as a template which others can use to build the illustrated material and in no way a comprehensive git tutorial.
"git-eliminate-head eliminates all downstream heads for a few forward-ported non-counted downstream indices, and you must log a few histories and run git-pioneer-object --pose-file instead. [...]"
This is a kind of very specific, targeted, niche comedy, excellently executed. I only use Git casually, and I can't tell how much of that text couldn't make sense in an actual doc; it all looked real enough at a glance.
In my experience, it gets better. After a while you know everything you need to not only do your job, but to understand and resolve common issues. After a couple of years you'll be at the point where the only time you have to dig through manpages is when you're trying to do something esoteric or clever.
Brilliant, absolutely had me rolling. Most cleverly-executed satire I've seen in a long while.
"To parse a staged SKIRT_SUBTREE and blame the working histories, use the command git-purchase-pack --snuggle-muster-branch, as after reapplying subtrees..."
> To rev-parse an automatic FLOUNDER_LOG or diff the working subtrees, use the command git-link-submodule --retrieve-wrestle-change.
I often overlook this detail when trying to hulk smash some broken change I've made upstream (what the manual entry correctly refers to as RIP_OTHER_TIP).
Git is one of the most amazing, powerful tools ever conceived, with one of the must byzantine and ridiculously designed 'interfaces' ever conceived.
People confuse the raw power of a tech, with how well it can be feasibly used. Sadly, due to the later issues, git will only ever be a shadow of what it could have been.
With all due respect to Linus, who'd be the first to admit he's not very good at UI stuff (I mean command line as well)... it's truly a sad thing.
This is a major 'problem that needs to be solved' I'm interested to see how it could evolve into something 'better'.
I don't think the complexity of git's command options is a UI problem. It results from the basis of its operation. We could change some names, add or remove some concepts to how some of the operations are performed, but there are simply a large number of actions to handle many edge cases.
A better solution for prose was to always be merging with live multiple collaborator updates. Conflicts are visible in real-time. I can't see something like this would work with code. Hmm interesting... unless we only allow additions and refactorings to working checkpoints.
Yes, I don't really understand all the people trying to "fix git". Git's fine, though the complexity makes it challenging, especially for new users. However the complexity is a direct result of useful features. "Keeping it simple" is great, except when the complexity is needed. I'm hard pressed to name any features I could do without.
There are two problems: one of inherent complexity, one of UI.
First - the UI is a mess, and that should have been fixed. It would make a big difference.
Second - is the inherent complexity. That's a good point, but I feel many things could be hidden or obfuscated.
Most poignantly, Git does something that most of us do not need: it was designed to work as a 'completely distributed system', i.e. for open source.
Almost none of us do that. 95% of uses cases related to you and I working collaboratively, on a project together.
The need to have repos which are essentially totally distinct from one another is a huge source of complexity and it simply doesn't need to exist in most cases.
So Git is basically an 'admin level tool' that is commonly used in scenarios for which it wasn't meant to be used, with a confusing interface.
It's costing a lot of time and money and headaches, I do believe someone may come along eventually and fix it.
This thread is essentially evidence of this - see how many people have difficult teaching what should essentially be a simple thing in most cases.
Way too many very smart people still spend too much time clustering around in git.
Ever used a centralized VCS, such as SVN? That seems to be what you want? A distributed VCS is extremely useful, for mine and many others' uses, however.
>I'm hard pressed to name any features I could do without.
Nobody is complaining about there being too many features. People are complaining about the arcane incantations that one needs to conjure to call them.
Good point. I wouldn't say nobody though. There are people out there that think there are too many commands. I've even seen academic papers that claim the staging area is problematic. I love the staging area, and don't think it's terribly confusing; there's always commit -a if you don't want to use it. It does lead to some confusion, but it's worth it for the added features.
That's just one example, but there definitely are people that think git's too featureful. As for more valid criticisms, I'd agree. I've heard the CLI compared to being in an abusive relationship. All that said, I can't really think of a better way to handle things without losing useful functions. In which case, I don't have any better ideas, and don't really know what I'm criticizing.
I like the painting analogy and have used it frequently.
Let's say you were commissioned to create a landscape painting. It'll be a big payday if you get it right and its due in 30 days.
Because you know you can make mistakes, you make a photo copy of your work every day and make a stack of them neatly on your desk. This photo copier is really high quality as copies are made at the molecular level.
On day 16, you find yourself working on the mountains in the picture. You sneeze and Oh no! You got paint all over your work. But since you have your copy pile, you just make another copy of day 15 and keep going.
But lets say you think its a lot and want a friend to work on the painting too. Just allow her to copy your latest photocopy and you both are off to the races!
Git seems complex but when you bring it down to a practical, non technical level, beginners pick it up faster :)
'How to teach Git' was something I had to think a lot about when a lot of my technical colleagues didn't know what a rebase was, and asked me to sort out their 12-team merge history (the 'git log --graph --oneline' spanned the width of multiple pages before you could see any commit message at all).
I realised that there's a point you can't progress beyond with git without having to do some thinking and learning. And lots of people aren't interested in the theory behind distributed file content. So I took Zed Shaw's hard way method and applied it to git. It made for a really enjoyable course at work and I then turned it into a book (1).
The tl;dr is I believe you need real world experience with a mentor prodding you at the appropriate time to think a bit deeper. Then the understanding will follow.
Humbly, I'd disagree that's the best, though this is a superb _part_ of the picture to teach Git well. These are nearly the last steps, I would say.
When new colleagues joined our firm and hadn't yet learned Git, the problems were always the same: uncertainty. They didn't know which Git operations were safe, and they didn't understand how to perform seemingly risky maneuvers with zero risk. They're used to even more dangerous tools that can wipe your work in a second - and, to be fair, Git can as well. The difference is that once you understand Git, you never have to worry about losing work.
So the way I would teach Git is to honestly start with the graph. Show it in action with pictures. Show how to always keep references to commits around to ensure work sticks around. Show how branching and stashing work, let them be confident that the tool will keep everything right where you left it.
_Then_, once they're confident in the basics, weave in the remote repositories.
> So the way I would teach Git is to honestly start with the graph. Show it in action with pictures. Show how to always keep references to commits around to ensure work sticks around. Show how branching and stashing work, let them be confident that the tool will keep everything right where you left it.
Personally, I think this should be coupled with teaching `git reflog` as the universal undo (as long as they don't `gc`).
272 comments
[ 3.0 ms ] story [ 256 ms ] threadI understand your second point, but I have a hard time understanding the difficulty with this part. Why is it hard for people to understand the idea of staging?
You put things in a box one at a time before closing the box. Does it require more explanation than that? What do people find difficult about it?
Many beginners will start by always doing "git commit -a" and that's fine, as long as they know there's an alternative once they need it.
Surely, most of the time when you go to commit, it's all the files you've changed?
EDIT: Now that I think about it, I also have several repos where I have changes that I never intend to ever commit them, because they are development conveniences for me personally.
I'm a JS dev mainly working in React on a web app with a backend team using PHP. Often I'll be working on a branch with maybe 2 or 3 people and I often end up working on a few things at a time. Say I'm working on a feature, and I notice some bug I'll fix that and then get on with my feature. Once I go to commit I pretty much always do a 'git add . -p' and I very rarely want to add all the files I've worked on!
Even things like switching a config file to use a service like apiary where I don't want to commit my change to the config to use apiary.. Or change to my webpack config for testing, etc.
I've used Perforce, SVN and Git and the whole 'staging' area thing always felt very natural to me. Here are the files you've edited, which ones want to be commited? It gives me a second chance to go through and check everything before I've commited, and often that stops me leaving in any odd comments or debug code.
What is the usecase where one needs to remember that selection for more than just a few minutes?
But the vast majority do, or at best become perpetual intermediates (https://blog.codinghorror.com/defending-perpetual-intermedia...).
99% of developers out there didn't need a power tool for source control (source control is already quite a power tool many devs can barely handle, even in SVN form...), yet here we are: Git is imposed everywhere, with its horrible UX.
People often then learn that there is a local file and some remote file: they can cope with a save -> upload workflow. Lots of traditional VCS turn this into a save -> commit workflow.
Git adds two stages to this that people can't see the need for without understanding the internals: an extra step between save and commit, and an extra step after commit.
(The discussion reminds me of all those people who think that if they just start by talking about monads then people will find Haskell easy and natural...)
1. Is my document saved?
2. Are the changes staged?
3. Are the changed committed?
4. Are the changes pushed to my fork on e.g. github?
5. Are the changes merged into the upstream repository on e.g. github?
However, I find the occasions for using the staging area in practice are few and far between, for the simple reason that I can't test and execute the code that's in the staging area without also having the code from the working directory also be there. It feels like after having partially staged some of my working directory, it would be a blind commit with no guarantee that things are working.
Very rare is the situation that I can break out a list of files over here that are for feature A and some over there for feature B, and never the two shall interact.
I think this is probably what most struggle with regarding the staging area, without being able to articulate it.
I do not see why a source control system should make it easier to make a commit that hasn’t ever existed on disk and thus cannot have been tested.
I think the better model would be to stash your changes and have an diff editor between the on-disk working copy and the stashed version that allows you to commit a set of changes as several smaller, more coherent commits.
That wouldn’t guarantee that each of those intermediate commits gets tested or even built, but it would guarantee that each smaller commit is in the on-disk copy at some time.
Not necessarily. One nice option that the git rebase command has is --exec (which can be specified multiple times). So you can run a rebase and have git execute a command (like running a test suite) for each commit in the branch. If any commit files, the rebase process will stop and let you amend the commit to fix the issue.
> or you have to do extra work (stash changes, test, restore stash) to do that.
I've found that it's easier to write and locally test a given feature and them incrementally stage parts of it and create commits before pushing the code up for review. To me, that's easier than just making a large commit and then trying to split it out into a better set of commits after the fact.
For example, I may write a new method and then call it several places in the code. So my first commit would be to add the new method along with its unit tests and my second commit would be to add calls to it in the code base and update the associated integration tests (if necessary).
Almost. Most often it's:
- Working on solving problem A - Notice problem B - Start to solve problem B - Notice I'm getting distracted from A, and return to finish it. - Want to commit my fix for A, but don't want to lose or forget the partial work on B.
Two different approaches I might take in this situation, depending on whether B is related to A.
1. If they are related (eg, B depends on A), use `add --patch` to commit A, then finish and commit B. 2. If unrelated, use `git stash --patch` to stash B, then commit A, then switch to a different branch to finish B.
Honestly, I see the point of both stash and staging, but not both together. Too many tools for the same job. On my long list of projects to do is a git porcelain that combines some of these concepts (eg, stash and working directory which would be tied to a branch):
- Each branch would have a single stash. - When you check out a new branch, all uncommitted changes are automatically stashed. - If the branch you're switching to has anything stashed, that stash gets popped. - Any current workflow that involves stashing can be replicated by using a branch instead of a stash.
This way, branches can be thought of as "state of the working directory", which is more intuitive with the branching tree model, imo; commits are a snapshot of the repo at that point in time; and the staging area is just a way to choose what should be included in those commits.
Git’s workflow wouldn’t even be sane without the staging area. This is what allows you to fix mistakes and make your work presentable for remotes.
I did exactly the same diff/tidy/diff workflow when I used p4 and svn, neither of which make a distinction between "working directory" and "staging area".
P4 and svn don’t have a strict commit parentage, which is why you can push commits in those systems in any order. Git’s strict concept of parentage is what makes the staging area so important for keeping your workflow similar to p4 & svn Workflows. Without a staging area, you’d either have to always fix mistakes with new commits, which is bad, or rewrite already pushed history, which is worse.
The terminology is a bit different - unless configured with mandatory locking (essential for some workflows) you don't have to open for edit. You just edit stuff and it goes in the "default changelist", roughly equivalent to automatic staging.
> Without a staging area, you’d either have to always fix mistakes with new commits
Mistakes at what point? In the normal svn workflow you can review with svn diff, then when you're happy do svn commit; it's just that there's no local place you're committing to. In both cases there's a critical point, either "svn commit" or "git push".
I’d guess you’re learning toward talking about svn, which I don’t remember very well, and I am leaning towward talking about p4, which always does mandatory locking.
You’re right the terminology is different between these different systems, I’m just pointing out that the git staging area has what you can think of as some equivalences in the other systems. Or, you can think of it as tradeoffs. Either way, the git staging area is something that helps you pretend like you’re using svn or p4 in the sense that it helps support editing multiple changes at the same time before pushing them to a server.
> Mistakes at what point?
With git I’m referring to mistakes between commit and push. But there’s a philosophical difference here that I glossed over. With git it’s easier to commit early and often than it is with svn or p4. With svn & p4 it’s easier to lose your work because version control doesn’t know anything about it before you push. If I make micro-commits, which I want and I like, then I put more “mistakes” along the way into my local history, and I can use the staging area to clean everything up before I push. With svn & p4, you make those mistakes and do the cleanup without ever telling the version control, and you run a greater risk of losing that work while you do the cleanup.
At work I’ll hit the squash option on gitlabs merge request which moots all local machinations.
You're adding a feature to your proggie. That involves modifying the main bits to add the feature and, say, adding a couple of interfaces to internal library modules.
Split out the changes to the library modules into separate commits---it's safe because nothing uses them, they're logically separate from the feature changes (although they don't appear to have a justification without the feature), the log will be marginally cleaner, and git bisect will have more granularity.
It's also possible to do this without the stash command, by making both commits right away, and testing them later. However, that would involve rebasing(?) your second commit on top of any changes you end up making to your first commit, so using the stash makes more sense to me personally.
The git stash man page talks about this: https://git-scm.com/docs/git-stash
“If you mistakenly drop or clear stash entries, they cannot be recovered through the normal safety mechanisms.”
One of the best things about git is how big the safety net is, as long as you tell git about your changes. Almost any mistake can be fixed, so why use features that aren’t sitting over the safety net?
Your example is an implementation of the box-putting algorithm, but it doesn't need to be mirrored in the put-box CLI.
This command could encompass all the putting and closing. Since you only close boxes when you are done putting things in it, I don't see a need or purpose to split it up. A closed box (commit) is always going to contain stuff that was put in it, so why separate commands?Anyway, `git commit file1 file2` by itself is most of the way to being the put-close-box function you want; it just doesn't work for adding/deleting files from the repo. Seems like they could make a lot of people happy by closing that gap and letting `git add` be an intermediate-level feature.
In Mercurial, I much prefer to just make it an actual commit in the draft phase (the default phase) and just keep rewriting that commit. Mercurial provides tools for both selectively adding and removing hunks from a commit (both `hg amend` and `hg uncommit` accept --interactive for hunk selection). If you're extra paranoid, you can make it a commit in the secret phase so it's not shared prematurely by accident.
It's pretty much functionally equivalent and doesn't require an extra location in which your code can be. It's either in your working directory or in a commit.
A bonus of this approach is that now you have a meta-history, hidden by default, of what you've "staged" and "unstaged". It's kind of like a reflog but with, in my opinion, a better UI. And of course, the index/cache/staging area in git doesn't use refs, so there's no reflog there.
If I had trouble with understanding the reason behind add->commit->push workflow, I would definitely have no idea what this article talks about when it says things like "merge, rebase, diamond shape". The flow chart looks almost exactly the same for "pull" and "pull --rebase". The only difference between the charts is the wording which has no meaning at all for a newbie.
"pull" and "pull --rebase" can cause two kind of conflicts:
1. Merge or rebase conflict inside the repository.
2. The would-be merged or rebased HEAD conflicting with the dirty working directory.
The article demonstrates the latter and it's the less important one as it's avoidable by pulling from a clean working directory or pulling a non-HEAD branch.
How to write a Good Commit Message (https://chris.beams.io/posts/git-commit/).
I've tried a similar approach years ago for teaching and failed spectacularly. Eventually, my peers became comfortable when they got used to the Github Desktop Client. They compared the buttons they click with my terminal commands. We also compared our graph views on Github website to visualize the logic.
It's been years and still none of them used rebase even a single time. A sad story in my teaching non-career. :(
For me, command line is freedom. GUIs are very limited.
Don't give up! For my humble experience:
- Try to know your peers, how they work, what difficulties they face when using command line, ...
- "I'm lost" > "git status"
- "How did we solve... ? > "git log"
- "This command is difficult to remember." or "This command makes no sense, I prefer this another name for that action" > "git alias"
Using a git GUI works quite well for people inexperienced with git.
GUI integrations have some advantages but the knowledge is not transferrable. Different GUIs work differently but the commands stay the same. I think both can complement each other. I use Pycharm a lot and it is a lot easier to see diffs or file history there. I think the same can be done with cmd as well but I don’t think I ever bothered to learn advanced commands.
While I understand why folks may want a GUI for git, I am continually amazed that there hasn't been an effort to "refactor" git commands so they're more consistent, easier to remember and easier to discover. It's a miracle that git has taken root so strongly, given it's shitty user experience.
I've been using git for years, and STILL, I need my cheatsheets and google far more than I would ever admit in person. Anything that's outside of heavily practiced workflow-- and I'm in a world of confusion.
I would posit the ed text editor for comparison. Why bother with a graphical text editor (including terminal editors like Vi and Emacs)? After all, you can look at the context surrounding lines you wish to edit by entering the appropriate ed commands.
I think it comes down to developers being so used to working with command line tools that they don't give GUIs a proper chance - ironically the exact objection they have with less experienced users not using the command line.
From UI/UX point of view, developers are users as well, but many seem to think developers should put up with bad interfaces.
Yes, once electric cars (with superior traction and brake control) take over automatic gearboxes (actually electrics don't even have gearboxes) will be the norm, but as long as there are engines driven on dinosaur fuel there will be a need for manual gearboxes. Which is why you should learn how to drive one. Once you're able to do that, driving an automatic is trivial, which was the point I was trying to illustrate.
I’ll answer that for you. It’s insightful.
In the U.S. today, less than 2% of new cars sold are manual now. Automatic gearboxes are well beyond the norm already.
https://www.chicagotribune.com/classified/automotive/sc-auto...
I didn’t understand why you said there will always need to be manuals for vehicles that run on gas... what do you mean?
At that percentage, the U.S. is a huge outlier however, and accounts for quite some chunk of of worldwide automatic gearbox equipped vehicle production on it's own.
The automatic gearbox remains the less popular option worldwide [1], though as you can see they are more popular than they were before.
[1] https://www.statista.com/statistics/204123/transmission-type...
At some point it's just Stockholm syndrome (we can only afford manual, everyone has it, so let's at least pretend it's cool!).
After all, as GC fan, it does not make much sense to malloc()/free() my gearbox. :)
The point being that what is right to learn today won't stay like that forever.
Only people that maintain old timers still know how to use a motor starter.
For example, I've never had any problems with Mercurial. Everything just works there and it is intuitive. You can never lose data there, and it has extremely sensible defaults.
I agree, and I wish there was a really good git GUI that either abstracted git nicely, or was as powerful as the CLI, or both. I’ve tried many of them, in production, and there aren’t any that give you a UI for everything git can do.
The problem I’ve noticed is that people raised on the GUI often don’t understand how to get out of trouble once they have a serious problem. Also the GUI tends to be a crutch that prevents them from learning the CLI well.
Git is natively a CLI and it really shows once you know both. Git’s CLI interface is awkward and hard to learn, and all the GUIs for git are somewhat awkward, both due to git being awkward, and also because trying to fit UI workflow onto CLI commands introduces awkwardness. All git GUIs are incomplete interfaces to git, there are none that give you access to all of git... specifically things like finding lost data and managing repos is something you’ll need to drop to the CLI for.
In class I'd stand on my right side, so students see me on the left (people are more used to left to right motions because of reading).
I'd demarcate the working directory, staging area and local repository non-verbally as 3 different spaces. My most right space (the student's left) was the working directory, my most right space (the student's right) was the local repository.
Everytime I'd make a transition from one space to another through add or commit, I'd make a hand gesture when I'd say "add" or "commit".
In order to add some humor (humor is rememerability) with push I'd point upwards to the ceiling as if I were looking to God. I'm not religious, but people got the reference and they all left out loud.
The last thing you want to do is turn them off entirely, or gatekeep the profession to exclude people who aren't good at reading dense documentation.
The best way to teach git is to get them comfortable with "Add, Commit, and Push" and then explain what's happening at each stage.
Maybe if somebody has already mastered subversion then git will confuse them a lot, but I'm not convinced even that is necessarily true. Regardless, it seems clear to me that novice users shouldn't be told that git is complicated.
Also "git log".
Of course four will work, it just adds more indentation.
"F*ck this shit, make your own UI on top if you want to use this tool!"
I don't think Torvalds has a proper excuse for the pain and suffering he's inflicted on millions of developers worldwide :(
(To the people going: "Oh, it's Open Source!". Sure, so are Mercurial, Fossil, etc.)
Another factor is that git was always FAST, and the most common operations (init, log, status, commit, checkout, push, pull) are not that complicated as people make them to be, so you can very easily start to use it for new local projects, and continue without installing additional plugins, unlike mercurial ... And at some point, when you wanted a colaboration hub or just a public remote repo, you just pushed your code to github.
Certain tools are organized by what code goes together rather than by what user functions make sense to go together. It's part of what makes it confusing.
Who are you exactly to judge people like that, oh failed incarnation of a tibetan Lama ???
He does not need an excuse to make a tool that has proven very useful to many (including me). In fact, I am grateful, that he chose to make it and publish it.
Actually this is exactly what happened back then, which is why many people used https://en.wikipedia.org/wiki/Cogito_(software) as a frontend to git in the early days. That said, I personally find current git UX absolutely fine and I can't imagine being effective without having all those commands like git reset --whatever -p and git rebase --interactive.
SourceTree tends to treat commits with multiple ancestry in a very weird way that has led to difficulty on multiple occasions, where people think changes 'went missing' because SourceTree decided the other ancestor wasn't important enough to show.
Also `git whatchanged` is a super helpful command to see just the list of files that changed in each commit
If you're interested, here's my .gitconfig, including all of my aliases: https://gitlab.com/lyndsysimon/dotfiles/blob/master/git/gitc...
has nice color coding around the date, person committing, commit hash, and message.
However, this only works with people who can make the mental leap to be able to deduce knowledge of what should be from knowledge about what is. In the end, I concluded it's a bit like teaching cooking. There are those folks who need to be taught about full recipes and those who need to be taught about resources and corresponding steps.
I have not yet seen a working approach to make both of these fractions happy, unfortunately.
There is a point, where you go from memorization (add, push, commit) to deduction (graph, objects and refs) but when this point is reached, depends on many individual factors.
[1] https://git.io/fhWxg
There was still obviously the issue of memorizing the commands, but at least I knew what the commands were doing on a deeper level.
[0] https://learngitbranching.js.org/
To understand the data structures interactively, use "git cat-file -p HEAD" and continue drilling down to an individual file in a subdirectory with "git cat-file -p OBJECTHASH"
Git fails majorly in this regard.
And how many people do that? Not that many. This angle is a bit like the guy who said that he doesn't want Unix file names to be UTF-8 text-only, because he crafted a sort of relational database on top of a Unix FS and by having file names be just text he couldn't do some super niche trickery. I think it was in response to this article: https://dwheeler.com/essays/fixing-unix-linux-filenames.html
> If svn is enough for you, that's fine.
It is, but every job these days forces you to use git. And most places I've worked at, git is used as a glorified SVN where people just have a 2-step commit to a remote server.
I regularly fix my commits, (just like I edit most comments on hn within a few minutes after sending them). And sometimes I take back commits even from the server (working in small teams).
Locally, I regularly switch to older commits and branches to try things out. In svn all this requires a network connection and it is quite costly. Which means you do a hell of a lot less of it. And I would assume it shows in software quality.
Man, how I used to freak out regularly waiting for a stupid svn diff or svn log to finish. These operations are the bread and butter of version control, and they are instantaneous in git.
Or, aren't you regularly blocked from doing quick fixes using svn for problems that you saw, but couldn't fix them because you had a different pending commit? I have that often when working with svn (admittedly I don't know svn very well, but I'm sure it is a real blocker in many situations). Situations like these are easy in git.
And how idiotic, after all, is it that we have to setup a SERVER to keep a simple log of changes to a few files? I have so many small ephemeral projects that I simply put in my laptop's home directory. There is no point in maintaining server repositories for those. Git is simply a tool that lets you do that. It lets you do bookkeeping of your data. It's a tool. While svn feels like an inflexible process. (yes, svn has the file:// protocol, but you still have to setup a separate repository, right?)
In a sense, git is the sqlite of version control. You are really missing out.
> It is, but every job these days forces you to use git.
Here in Germany, svn seems to be the norm still in the engineering domain. But I figure the situation is quite different for web shops.
> And most places I've worked at, git is used as a glorified SVN where people just have a 2-step commit to a remote server.
That's my experience as well. When it's about communicating changes most setups will be centralized just like a regular svn setup. It's a fine approach for small teams. But with git you still have the huge advantage of being independent of the server. And you can easily fix problems that are already committed to the server.
"Git sucks, the UX is atrocious, I don't want to spend half my life learning a tool that shouldn't even need that much hand holding."
"Learn Git!!!"
It may seem paradoxical at first to you, but is true (as are many things in this profession). Another paradoxical advice like that, is to learn vim, or emacs, but I digress.
Git does not suck - as any other tool it, it just has strenghts and weaknesses (for example working with very large binary assets is its main weakness).
The UX of most common git CLI operations is clean actually, as they are fast, and you do not need many arcane options (although they are there, and are documented well, for people who read...).
If you screw up something, you just use the reflog to fix the state of your repo in most cases. Even if you can not (or do not want to), the troubleshooting is still easy - you can always do a fresh clone from your remote repository in a new folder and copy what you want there.
Regarding Git, Git does suck. It does the job Linus designed it to do, but that job is not most software engineers across the world need it to do.
In smaller or in corporate shops, Subversion was almost adequate and several bad implementation details, mostly related to branching, led to its demise. So that world needed Subversion++, not Git.
In the FAANG world, there's basically no company that uses Git as-is. It's strength/weaknesses aka tradeoffs aren't good enough for them.
Git won because tech is a popularity contest and people in our domain like to do a lot of virtue signalling ("this tool is hard to use, I use it, so I'm special/cool").
What do you think is wrong about it? Or have you only "read a ton" but never used it for a while? In the latter case, I suggest you start with the things I mentioned above, and make good use of "git reflog" as suggested by somebody else. If you know git reflog, "delete tree and clone a fresh copy" is not a thing anymore.
> In the FAANG world, there's basically no company that uses Git as-is. It's strength/weaknesses aka tradeoffs aren't good enough for them.
Do they use svn then?
>> It does the job Linus designed it to do, but that job is not most software engineers across the world need it to do.
Speak for yourself, you are not most software engineeers.
>>In smaller or in corporate shops, Subversion was almost adequate ...
I have administered SVN profesionally for several years (2005-2007), and was paid to unfuck screwups made by other developers using it (which many times involved restoring from incremental hourly backups done on the SVN server side). Dealing and helping others with their git problems is many times easier.
The FAANG world (which I had to google just now) I imagine has unique requirements (many teams that must coordinate, super gigantic legacy source code base), and the resources to do whatever they want (money, humans to develop and maintain tools and do research). For them, the integration pain from managing multiple smaller repos may be significant. Outside this world however, teams are more independant and the source code size is much much smaller (even for legacy projects).
>> Git won because tech is a popularity contest
You have a point here, but this factor (and network effects in general) is just inertia, and does not explain why git won, given that for example SVN or Perforce had such a head start (in tooling, and in mindshare), and there were other distributed contenders like mercurial and darcs and BitKeeper developed at aproximately the same time, and even earlier. It won in my opinion because it was simply superior tech - faster, good enough and very very easy to get started.
(edited to clean up formatting)
However, what you do need to understand is the model that git uses, which is extremely simple.
Git provides you a way to store snapshots of data into an on-disk graph data structure* that you can sync to and from remote repositories. You also get refs to store symbolic references to the snapshots in the data structure, and you can sync those too.
That's pretty much my mental model of git in its entirety, and it allows me to merge, branch, rebase and perform all kinds of commit surgery with ease, because I can always tell what effect an operation is going to have on my data structure (and even if I'm wrong, I can't lose data)
I seriously can't see how it could get any simpler.
(*) a git repository can actually contain several separate graph data structures, but that's usually not what you want...
https://git-scm.com/book/en/v2
But git isn't complicated. Git is a handful of simple ideas composed in interesting ways. It looks complicated because there's a lot of porcelain commands with a lot of options, but all of them are just manipulating the same simple internals which, once understood, are clear and intuitive.
On the other hand, I've used ClearCase.
(Do not use ClearCase.)
Thing is, dealing with git feels like being in the early 2000's using Clearcase views.
Substitute C, Java, Python, etc for git. You can probably do something with those languages, but you aren't going to get very far without reading some sort of documentation.
It's also very important to learn to use the reflog. When I was learning to climb they told me I'd never get really good until I'd fallen once. The same thing goes for git. People are really scared of it because they think they could lose work or something. Thanks to the reflog and the way git works, that's actually quite difficult to do.
Git will garbage collect these commits that can't be reached by any reference after a while, but usually that's long after you've forgotten they ever existed.
There was also a disagreement about what the semantics should be, e.g pre-receive.d/* hooks failing on the first one that failed in glob() order or not, which has implications for whether they can run in parallel.
The bit that stuck from those patches was being able to configure the the core.hooksPath variable to a hook path, which sometimes can with some stretching fulfill some of the use-cases, but most users end up with a meta-hook runner of some sort.
Parallelization is optimization so I think «make it right, then make it fast» is sound advice.
In any case, even if glob() wasn't portable it's easy to provide an API compatibility layer that's just readdir() + qsort(). That part would be trivial, and might perhaps be needed due to collation issues in the sort.
Doing it in parallel (preferably in a random order) isn't just an optimization, but would ensure that there isn't an implicit dependency on whatever iterative interface we'd first ship with.
1. http://pubs.opengroup.org/onlinepubs/009695299/functions/glo...
2. https://developer.apple.com/library/archive/documentation/Sy...
Reading this article, it occurs to me how useful the idea of a "staging area" would be to helping them understand. I don't think of it that way myself when I'm working with it (I suppose I do, but not in those precise terms). But looking back, that's what was tripping them up. If you're just talking about local and remote repositories, you're not really giving them the right idea of the workflow.
There are people who love illustrated explanation and for those these are perfect. This is just meant as a template which others can use to build the illustrated material and in no way a comprehensive git tutorial.
https://blog.hackensplat.com/2018/12/git-isnt-perfect-and-ot...
(Addresses some of the points raised in this article.)
https://git-man-page-generator.lokaltog.net/
"git-eliminate-head eliminates all downstream heads for a few forward-ported non-counted downstream indices, and you must log a few histories and run git-pioneer-object --pose-file instead. [...]"
Thank you for sharing, this made my week.
but maybe that's due to the low bar that it's being compared to.
"To parse a staged SKIRT_SUBTREE and blame the working histories, use the command git-purchase-pack --snuggle-muster-branch, as after reapplying subtrees..."
> To rev-parse an automatic FLOUNDER_LOG or diff the working subtrees, use the command git-link-submodule --retrieve-wrestle-change.
I often overlook this detail when trying to hulk smash some broken change I've made upstream (what the manual entry correctly refers to as RIP_OTHER_TIP).
[1] https://git-man-page-generator.lokaltog.net/#81394c8bf3806f9...
Git is one of the most amazing, powerful tools ever conceived, with one of the must byzantine and ridiculously designed 'interfaces' ever conceived.
People confuse the raw power of a tech, with how well it can be feasibly used. Sadly, due to the later issues, git will only ever be a shadow of what it could have been.
With all due respect to Linus, who'd be the first to admit he's not very good at UI stuff (I mean command line as well)... it's truly a sad thing.
This is a major 'problem that needs to be solved' I'm interested to see how it could evolve into something 'better'.
A better solution for prose was to always be merging with live multiple collaborator updates. Conflicts are visible in real-time. I can't see something like this would work with code. Hmm interesting... unless we only allow additions and refactorings to working checkpoints.
First - the UI is a mess, and that should have been fixed. It would make a big difference.
Second - is the inherent complexity. That's a good point, but I feel many things could be hidden or obfuscated.
Most poignantly, Git does something that most of us do not need: it was designed to work as a 'completely distributed system', i.e. for open source.
Almost none of us do that. 95% of uses cases related to you and I working collaboratively, on a project together.
The need to have repos which are essentially totally distinct from one another is a huge source of complexity and it simply doesn't need to exist in most cases.
So Git is basically an 'admin level tool' that is commonly used in scenarios for which it wasn't meant to be used, with a confusing interface.
It's costing a lot of time and money and headaches, I do believe someone may come along eventually and fix it.
This thread is essentially evidence of this - see how many people have difficult teaching what should essentially be a simple thing in most cases.
Way too many very smart people still spend too much time clustering around in git.
Nobody is complaining about there being too many features. People are complaining about the arcane incantations that one needs to conjure to call them.
That's just one example, but there definitely are people that think git's too featureful. As for more valid criticisms, I'd agree. I've heard the CLI compared to being in an abusive relationship. All that said, I can't really think of a better way to handle things without losing useful functions. In which case, I don't have any better ideas, and don't really know what I'm criticizing.
https://www.atlassian.com/git/tutorials
Let's say you were commissioned to create a landscape painting. It'll be a big payday if you get it right and its due in 30 days.
Because you know you can make mistakes, you make a photo copy of your work every day and make a stack of them neatly on your desk. This photo copier is really high quality as copies are made at the molecular level.
On day 16, you find yourself working on the mountains in the picture. You sneeze and Oh no! You got paint all over your work. But since you have your copy pile, you just make another copy of day 15 and keep going.
But lets say you think its a lot and want a friend to work on the painting too. Just allow her to copy your latest photocopy and you both are off to the races!
Git seems complex but when you bring it down to a practical, non technical level, beginners pick it up faster :)
I realised that there's a point you can't progress beyond with git without having to do some thinking and learning. And lots of people aren't interested in the theory behind distributed file content. So I took Zed Shaw's hard way method and applied it to git. It made for a really enjoyable course at work and I then turned it into a book (1).
The tl;dr is I believe you need real world experience with a mentor prodding you at the appropriate time to think a bit deeper. Then the understanding will follow.
Here's an example from an 'advanced' chapter: https://zwischenzugs.com/2019/01/09/git-hooks-the-hard-way/
[1] https://leanpub.com/learngitthehardway
When new colleagues joined our firm and hadn't yet learned Git, the problems were always the same: uncertainty. They didn't know which Git operations were safe, and they didn't understand how to perform seemingly risky maneuvers with zero risk. They're used to even more dangerous tools that can wipe your work in a second - and, to be fair, Git can as well. The difference is that once you understand Git, you never have to worry about losing work.
So the way I would teach Git is to honestly start with the graph. Show it in action with pictures. Show how to always keep references to commits around to ensure work sticks around. Show how branching and stashing work, let them be confident that the tool will keep everything right where you left it.
_Then_, once they're confident in the basics, weave in the remote repositories.
Personally, I think this should be coupled with teaching `git reflog` as the universal undo (as long as they don't `gc`).