Flight rules for Git
github.com
github.com
1. Git, a software product with more footguns per square inch of user-exposed surface than any I've ever encountered, ended up winning the distributed VCS sweepstakes; and
2. Even now, more than five years after it was clear to everyone that Git had won, there is absolutely no momentum to go back and clean it up to remove any of those footguns. All we get instead are increasingly shiny GUIs to hide them under, and well-meaning blog posts advising you on which is the best kind of tourniquet to apply when you trigger one of the aforementioned footguns.
Sigh.
git's wall is thick, made of brick and reinforced concrete, and you'll be heading toward it at 200kph. You'll get going again eventually, once the metaphoric car gets glued back together.
Non-metaphorically, my experience has been that with SVN, Mercurial, et. al., a few quick web searches and I'm on my way. With git, "wait a minute...run that by me again?" I've used git for going on a decade, and now that I'm in an SVN shop I do sorely miss it. But, man, it's got some sharp edges.
Up until this day navigating code feels more natural on the site that on native apps like sourcetree. I really miss the ease to see the same file from two different branches or commits, the feature might be there in native apps but I just couldn’t find it.
I know, right? This 'evil' thing is going places.
But the tool is not conceptually difficult. All the difficulty of git is in the trap its "UI" creates for no reason. The concepts are easy. I taught Darcs to a frontend/flash developer 15 years ago, and it's not because I'm a great teacher.
Conversely, attached HEAD means a branch is being updated whenever you make a commit, e.g. when you make commits on your local master.
When you move out of detached head state by checking out another branch Git tells you how to save your work to a branch.
It could very usefully try harder to avoid getting into that state in the first place.
That's a great idea and it really confuses me why they don't add obviously useful things to the interface.
Am I missing something here? How do you get into a detached HEAD state without explicitly taking an obviously weird action, like finding and checkout out a commit hash instead of a branch, and why would it make sense for git to not be in a detached HEAD state should you do that?
Most people don't think about doing a hard reset to the last good commit, and then soft resetting to HEAD~. Instead, they just "git checkout $LAST_GOOD_COMMIT". And afterwards they may even continue by working with a detached HEAD
Several times that I've helped junior developer out of a detached HEAD state, when I asked what they thought they were doing before it happened, the answer was "I was just doing my normal rebase before..."
But this isn't an obviously weird action? It happens all the time when you need to branch off from a previous point.
Maybe you're unexpectedly releasing a hotfix, maybe your current development is going in a bad direction, maybe you need to test something against a previous state.
I checked out master but now my commit is no longer there. HELP!
Can I somehow push my changes to you so you can fix it?
Ah let's intuitively try:
git log --graph --decorate $(git rev-list -g --all)
For comparisson:
- mercurial would show your commit all the time, no matter what currently is checked out.
- Only when you push, you have to force it, to create a new head in the remote repository because most workflows expect you to merge heads locally.
Detached is just that: you can party on it all you want, but it's not going to go anywhere when you commit unless you go out of your way.
Every digital damage can be undone by wasting enough time.
touch -- -l
ls * touch -- -l
ls -- *
The fact that you have to use a double-hyphen flag to create a file with a name starting with a hyphen is a strong indication that this situation is an edge case. rm master
git checkout -- master
git checkout a1b1
git checkout master -- master
all works as expectedYou'll note that, for example, your "rm" didn't require a double-hyphen.
The OC's point (and maybe the GP's point [1]), ultimately, is about git command syntax ambiguity. With traditional Unix commands, there's not much ambiguity. At worst, there's merely no obvious way to pass an argument that's a filename starting with a hyphen. The question "what does 'ls -l' do?" would not plausibly have an answer of "output '-l' if a directory entry with that name exists" in addition to "output a long listing of all directory entries".
[1] Does git actually behave differently based on the existence of a directory entry named "master" or does it merely require extra effort to work with such an entry, which would actually be directly analagous to filenames beginning with a hyphen?
* Checkout a file called a/b
* Checkout branch b from remote a
* Checkout a local branch called a/b
I've seen people end up with local branches with names like 'upstream/mybranch', then get super-confused about how these branches aren't "upstream". git should really ban local branches with a / in their name.
As a polygot developer working in multiple languages, editors and ecosystems, git has been the one tool that I'm always using, and that makes it always worth investing more time into learning how to use it better.
"This merge went completely sideways. But I haven't pushed anything."
"Okay, do this to export the commits you made as a patch file. Move those somewhere safe. Delete your repo. Reclone. Apply the patches. Commit. Start merge over again."
Also I can recognize whatever that weird error message is that means someone deleted a file in one branch and I edited it in another. Usually results in just "git rm (file); and git merge --continue".
git merge --abortJust think how many person-hours were wasted by that team huddled around the monitor...
GIT: The Swiss-Army Chainsaw of Version Management.
It's also the easiest way to understand git.
You could learn to read by memorizing the shapes of thousands of words without learning the alphabet or how it matches up to sounds. I'm sure people have done it. But the failure mode when encountering a new word is complete and utter bewilderment that this new shape is unfamiliar, rather than a graceful degradation you get if you understand the alphabet. The same is true of git.
The UI does a remarkably good job of hiding the elegant underpinnings from you.
In this case, I have a single suggestion for you: Give the following extra arguments to git clone.
--reference /path/to/existing/clone/ --dissociate
This will download objects from your existing clone rather than from the remote repository, thus saving you a great deal of bandwidth if your repo is appreciably large. Also, this operation is absolutely safe. It will only copy objects over into the new clone that are referenced in the remote repository.Ever sat through user training on microsoft office? How to use email? The weird one-off enterprise apps that nobody else has ever heard of?
They’re unbearably tedious to users that can get by in a new tool with a 5 minute crash course, intuition and light googling.
Then git shows up and has the audacity to ask professionals to learn its mental model.
Once you do the interface isn’t great, but not particularly esoteric. The problem isn’t that users see dense-sounding things like “directed acyclic graph of revisions”, it’s that they assume they can competently manipulate one without knowing what that means.
I do think the interface could use cleanup, but can any ammount of cleanup prevent needing to know the mental model of git?
Git's interface is far, far more complicated than its model. Personally I spent years not learning about the model because I thought it would be as complicated as the interface. When I realised that the model is basically as simple as what you get when you view the "network" on GitHub then I started loving Git.
Mercurial has the same "DAG of revisions" model as git, and works just fine, because it has a good UI.
Darcs has a more unusual mental model than git, and works just fine, because it has a good UI.
People keep saying this, and this keeps not matching my experience of helping people with VCS issues at Mozilla.
Mercurial's model is roughly "append only list of commits, that separately form a DAG". This list model is leaked all over the place. For example the default `hg log` view just dumps that list rather than telling you anything about the history of the current working copy. To get that you need to add -f, which of course in most contexts means "do something unsafe". Worse, the list model is exposed in the local revision ids, which are prominently presented in the UI, and users have to learn that these ids aren't sharable with other users when discussing a revision. Also the most prominent "symbolic ref" in mercurial is "tip" which means "commit at the last position in the internal list", and so is sometimes the current head, and other times is some random commit from the remote.
Giving people help in mercurial is also super-challenging because, unless you are in some controlled environment, the first question is always "which extensions do you have enabled" and for complex tasks the next step is often "you should enable and learn to use <some extension>".
Which isn't to say that Mercurial is overall particuarly bad. There are some areas in which it's really an improvement over git (revset specifiers are pretty awesome, for example). But I often feel like praise for the UI comes from people who have internalised its flaws, and is repeated by people who haven't actually used it, but dislike git and are happy to accept something that confirms their preconceptions.
There are a couple more flaws. The commandline is not very consistent, and the complete model is way too complicated IMHO. I can work with it now, but it took me a long time to grasp all of the concepts that I need - without feeling the need for distibuted version control. About that: It's kind of ironic that git is hailed for being distributed, but everyone just uses github, a centralized system.
Edit: You're right, but my point was that the revert command treats a commit like a set of changes, not like the state of of a repo (since you can't revert the state of a repo, but you can revert changes).
If you checkout a commit, the commit represents the state of the repository. If you revert a commit, the commit represents a set of changes.
It's fun to be cynical, but surely this is just yet another example of how hard it is to "just change" something for all sorts of reasons.
It's funny that you bring up hubris when the pinnacle of developer hubris is "surely it can't be too hard to {build it, change it, replace it} -- I could do it!"
I’ve never found myself backed into a corner and I suspect this is part of its success: some people have codebases where it’s a problem (branch names which are files, for instances) but most people don’t encounter this sort of thing and it works just fine.
Flight rules, process workflows, protocols (both network protocols, and protocols of mission critical human activity), they all belong to the idea of limiting yourself to a set pattern of activity when dealing with something that can practically give you infinite possibilities. Because infinite possibilities give decision fatigue and analysis paralysis.
By 2D code I mean code in a text editor: the two dimensions of lines and columns.
By 3D code I mean: lines, columns, time (meaning commit timestamps)
By 4D code I mean: lines, columns, time, time travel (you can go back to older commits and rewrite your revision history).
When I first started using Git, I was told, "When you want to make you commits public, use `git push -u origin <branch>` where branch the branch you're pushing to." That advice is technically correct, but I had no idea what -u nor origin was. I took 5 minutes to decipher the command. I walked away understand that origin an alias for the remote server, and that -u associates a remote branch with your local.
I don't think Git is the greatest thing ever. I do think there's a bit of a learning curve. I also think that it's not as complicated as people make it seem. This post is a great reference for how to do X in Git, but it's a terrible source for beginners learning how to use Git. Cheat sheets are most effective when you have a basic understanding of the material.
If nobody understands what the commands do, the problem is with the commands, not with the people.
`git push -u origin <branch>` requires you to to understand tree things: push, remotes, and branches. Once you know what those are the command is straightforward. Those concepts are very fundamental to any DCVS. Maybe you can do the first steps in other DCVS's without this knowledge * , but doing everything but the trivial things will require you to understand these concepts.
It's not strange that a tool solving a complicated problem for professional developers requires a bit of investment.
* But so you can in git, learning this command by rote is a valid strategy for a new git user.
There are those who say you should never rebase as a form of simplifying git, but people who follow that workflow have much much messier histories than those who know how to rebase properly. A well-curated history of well-explained atomic commits has incredible value, far outstripping the cost of maintenance on a long-lived project.
Git is a professional tool for software engineers, it has severe short-comings for non-coders, but as a software engineer learning it well will magnify your value.
Either Git is intended to be a tool anybody can pick up and use, in which case it's shameful how thoroughly ignored widespread complaints about the UI have been; or Git is intended to be a tool for a select priesthood of Enlightened Masters, in which case it shouldn't be promoted to the unwashed masses. You can't really have it both ways.
> Git is a professional tool for software engineers
Lots of tools for professionals of all kinds take the time to protect their users from inadvertently harming themselves through misuse. Cars for commercial drivers aren't sold with a set of wheel chocks in place of brakes, and if you argued that they should be because they're meant for professional drivers who ought to know better than to walk away from their vehicle without setting the chocks first, you'd earn yourself a lot of funny looks.
I don't understand how anyone can live without rebasing their branch on master so their PR is a fast-forward merge and squashing their 4 days of WIP and Changing Computers commits into one sane commit.
Let's not even mention the fact that they introduce all those in-between commits where your unit testing is broken.
Our team has a very strict rule that every commit on master has passing unit tests and all merges to master (within reason) are fast-forward merges. We also allow force pushing to your feature branch to encourage using git as a decent backup for your WIP and eventually the cleanup before merging. As such, newbies get taught the basic rebase commands. This requires talking about the underlying model of git.
Cheap jab aside, yeah git is fine 99% of the time. But that last 1% can really fuck you. 1% is not that small percentage.
You already mentioned the reflog, which makes Git screw ups a non-issue. But the only time I seriously messed up something was when I used Git commands without full understanding what they actually did (two weeks into learning Git). This was mitigated by RTFM, which clearly explained what I did wrong. Since then I never listen to advises like 'just use this git command', but read the manual first.
I noticed that most people who have problems with Git don't understand basic Git concepts like branch, remote, staging, commit, ref. These concepts are really not that hard. Once you get familiar with them everything else with Git is a breeze. But I'm surprised how stubborn people are at not willing to spend a little time to learn. They just want you to tell them Git commands. But Git commands are not important. I personally don't remember exact command parameters, because I can always look them up in the man page. Git concepts is what makes you proficient with Git.
I started distributed version control with monotone. That felt better but the central db felt a bit unnatural.
A bit later came mercurial and I was really happy with it. It all made sense. Easy interface and when you wanted to do something strange like rewrite history, you had to use a plugin.
Then came git (rhymes to better know your sh*t). I'm not surrounded by the smartest guys but most people I met only have a superficial understanding of git and looking at these flight rules kind of confirms the complexity of git.
I strongly believe that 80% of the current users of git would be better off with mercurial. But then everybody uses git for the remaining 20%.
edit: They must have been using a different SCM; ignore me!
Perhaps you're thinking of a different meaning of "branch"?
* git has clones
* git has bookmarks, that's "normal" git branches
* git has anonymous branches, it's called "detached head" and is completely broken
* git does not have named branches (that's the branch name being part of the commit's immutable metadata)
To a git user, what's the rationale and when is this useful?
For example you can make a seemingly unrelated change in a feature branch.
And then later wonder why this change has been done. Of course you could hope to find the merge into master where the feature name could be mentioned. But it’s nicer to have it on the commit.
=> it gives extra context to the commit
That said, I'm somewhat disappointed that cloning has fallen out of favour and it's standard to use an upstream server (usually GitHub) not only as a central authority but also as intermediary for sharing code. Apart from "forking", cloning repos has practically been delegated to history.
I concur. I was in a team that needed to migrate away from SVN. I was already privately using mercurial while in the team. Jumping to git was too much for them in complexity. I tried convincing them that mercurial will give them almost all the benefits without the (needless) complexity, but they wouldn't listen and ended up using TFS.
Looking at https://confluence.atlassian.com/get-started-with-bitbucket/..., the conceptual model looks almost identical, and so do the commands for common tasks.
- working directory
- staged
- commited
- (upstream)
*
hg has only 2+1 - working directory
- commited
- (upstream)
*
(*) both support something like stash but in hg it is easier to avoid that.git log shows you history depending on the checked out version
hg log shows you the whole history of the branch, also after your checked out version. Knowing where you are is very valuable.
in hg you can push multiple heads into a repo. that is very convenient when a junior is unable to merge something. to my understanding in git you need different branches.
git - linus wants you to edit commits until they are perfect
hg - mpm once said that you have to accpet mistakes that happen[1]
as a consequence git has more advanced features very exposed while hg has a simple novice workflow
[1] https://www.mercurial-scm.org/pipermail/mercurial/2006-June/...
Incidentally, using shelve in hg is the only way I've ever lost work in a VCS (I've made the same error in git as well, but a dropped stash can be found again).
> git log shows you history depending on the checked out version
> hg log shows you the whole history of the branch, also after your checked out version. Knowing where you are is very valuable.
git can show this, too, if you ask for it. If you've checked out some ancestor of `branch-name`, then `git log branch-name` will show `HEAD` next to the commit you've checked out. A single commit can belong to multiple branches, so if git showed you commits after your checked out version, which branch should it show? All of them?
> git - linus wants you to edit commits until they are perfect
You can use a never-rebase workflow in git as well, but most people elect not to.
At least today there is
.hg/shelve-backup/Read the git book, too. It's free and serves as a good introduction and helps you learn which man pages you need to consult and when. You don't need to read the whole thing, but the first 3 chapters are indispensible.
Why aren't reverts more widely used? It seems a lot of devs I've worked with don't understand them, and to me they seem so simple and are so much safer than destroying/rewriting history.
Not only does it preserve the change in an easy-to-see manner, but you can also provide more context on why you undid your commit (my revert messages often read like "Revert "previous commit message here" due to it potentially causing some older android devices to break. More info at ticket #1234")
Now not only do you have a clear history of what happened (someone added X, then you undid X at a later date), but you also have context for why the change was undone, and can either revert the revert if you need to, or just re-do the work in a new commit later.
It's a great "oh shit" button that I'm much more willing to use since I know I won't have to worry about "rescuing" the commit later. If something goes wrong and I have a hunch it was caused in a commit, i'll often just revert it and investigate later when I have time.
See "All the things your branch has ever been" http://h2.jaguarpaw.co.uk/posts/git-survival-guide/
a revert keeps that history forever in your repo (unless you do something to explicitly delete it), which IMO is just as important as all other commits.
1. Developer writes some code in a branch.
2. Developer merges code into master before the code was actually ready to be deployed.
3. Developer reverts the merge commit in master to roll back everything.
4. Developer fixes the code in their branch.
5. Developer merges their code into master.
6. Their code doesn't show up in master, since it was already merged in and the revert is part of the official history of the branch in master.
7. Developer has to make a revert of the revert and then untangle the mess.
In your scenario, it's a puzzle to unfuck, but it's unfuckable after a bit of work. In the alternative you run the risk of losing work, or blowing out other's commits and causing work for multiple other team members.
I guess my point was that I feel "revert commits" should be treated as the default, and the goto in most situations, and only using something like `git reset HEAD^ --hard` when you really need to, or a revert won't do. I'll be honest, I sometimes use a "reset and force push" when I've fucked up an ugly merge, or I want to undo the last few commits at once, but it's the exception, not the first tool I reach for.
For people having difficulty with git, I recommend going through the official book [0] on a couple of topics with a toy repo and play with the tool a little bit. Another alternative is to find a simple workflow, and stick with it until you have time to explore git more.
Compared to other tools I've had to use in the past (AccuRev ughh), git is fantastic. Just being able to see what files have been modified without waiting for 10min+ or needing to be connected to a server changes your workflow, but we've probably become accustomed to those benefits, but still notice the arguably crusty command line.
I joined a new small team several weeks ago. All of us are fairly new to git, coming from cvs and svn backgrounds. We use Intellij as our primary IDE, and bitbucket as our remote origin.
Do y’all recommend using the IntelliJ git front end GUI, or calling git from the command line?
I thought I had a grasp of git from some side projects on GitHub, but now that it’s a key dev tool in our daily process, the more I learn about it and need to extend beyond simple checkout/commit/push workflow, the deeper the rabbit hole goes.
Use a text editor eg. Code to fix merges.
Git is powerful. And can have a hard learning curve, but don't have to. Learn it in CMD.
I mostly use
status push pull commit -a add checkout checkout -b branch list merge
Gitk and git GUI usually comes with most installations and works for Mac Windows Linux. Then any other UI is just sugar.
As the sibling said, learning the command line first (and the model of Git) is mandatory, especially if you come from a CVS and SVN background. I came from Perforce and had a pretty hard time with Git until I invested two days with a good tutorial and much experimenting with two local and one remote repository.
EDIT: spelling
I used to remember command in certain situation and have a hard time understanding stuff like stash, rebase, cherry-pick, detach head etc...So whenever come up I have to sit google the heck out of it.
Then one day I decide to sit down and just read Git properly. They are on: https://git-scm.com/
Eventually I understand it better and now I know what I need to do in certain situation.
Read chapter 10 of pro git
Mourn enlightenment