Like, I've had to explain a lot of times why you `git pull origin master` but when you want to interact with that remote branch otherwise it's `origin/master` instead. The lack of clarity is in what commands operate on what levels, with many of them operating on several at once.
There have been some efforts to reform the command set to be more clear, like `git switch`, but the old commands will persist forever along with a lot of other footguns (like `git push --force` really ought to be replaced with `git push --force-with-lease` and moved to `git push --force-I-really-mean-it` so it hardly matters.
As part of a security-related project some years ago, my team and I hacked jgit to use SHA256, which required changing the length of pretty much every on-disk data structure. Sadly, there was (probably still is) no HASH_LEN constant, just a lot of magic offsets strewn throughout the code. I had to compare lengths against the git spec at every step.
And yet I still scramble for stackoverflow every time something goes slightly amiss.
jGit is actually a separate project from core Git, but once it gets adopted into core Git we can expect that jGit will follow suite, given that it's critical to Gerrit and other projects.
It was actually a pretty cool system. I don't think it was ever sold though.
Wow, that description feels spot on.
This is the crux for me. Command naming is completely unrelated to and unindicative of state.
It feels like surely there's an opportunity for the basic CRUD operations to be collapsed down into a standard "{action} {source} {target}" style.
There will be nuances, specifically around branching, but the basics should be basic. As opposed to a Swiss Army knife, where you have to pull out the scissors and squeeze them three times before you can unfold and use the blade.
If it's difficult to keep your mental model of some system up to date, I doubt that doing bigger steps at once makes things easier.
So
1. run `git fetch`
2. if the textual output does not tell you what has happened, run `gitk -all`
3. Decide what to do. Rebase, merge, whatever.
Of course if you know exactly what you are doing, pull can be fine. If you changed the repo yourself on another computer that is the case. Otherwise, how can you know your second step, before having even seen the data you are operating on? Well, it can work, but if it doesn't, don't complain.
I agree. For a DVCS like git, separating the network transaction from updating the working copy on disk is the best way to go about it. Going in the other direction, this is the default since git add, git commit and git push are executed separately.
Essentially the whole concept of "upstream" is weird and non-orthogonal. Another one that bothers me is that as far as I can see there's no way to globally turn off setting an upstream on newly created branches (I can pass a flag to the specific "git branch" command, but that's tedious and error-prone).
`git fetch` (and by extension `git pull` when given a remote) and `git push` copy data to and from a remote. When you specify `git pull origin master` you're saying "pull down a copy of the remote ref master from origin", which it then saves locally as the ref `origin/master`.
Everything under `origin/` (or really `refs/heads/origin/`) is just a cached pointer to the last known state of that ref on the remote.
All other commands operate only on these local references. So when you want to refer to what you know to be the state of things on `origin`, you can use `origin/master`. Otherwise that command has no particular knowledge of how to talk to origin.
Incidentally this is a shortcut I use all the time to update my local master from a remote:
`git fetch origin master:master`
Which is super unclear in its meaning but it means fetch origin's master HEAD and put it in my local master ref. I actually use this more often than git pull nowadays.
Pushing merges is great. Pushing random (unreviewed) local commits directly to master is bad, but it's no worse when those commits are merges than when they're not. Conversely, rebasing master (which is quite easy to do if you're inexperienced but have been advised to use git pull --rebase) and pushing that creates a self-perpetuating mess that is very hard to fix (because even if you fix what you did, any other user who did a rebase-pull of master in the meantime is going to reintroduce the problem). Using rebase also trains you to force-push which makes messing up published branches much easier.
- origin master <=== the actual remote version of the master branch
- origin/master <=== a local branch that you cached from the "origin master" remote, may or may not be in sync with the real "origin master"
`git pull remote_repository_name branch_name` is the generic way to look at it instead of some magic incantation.
I like to call origin "upstream" to differentiate them.
and then git pull is another way to think of git fetch and git merge as one command roughly.
Comparing against that locally cached ref is also what git uses to tell you how far behind/ahead of the upstream you are in `git status` or whatever. Fetch and push are the only git commands that actually talk to a remote (at the "user level" of the command set anyways, those are also composed of lower level commands).
It is worth the time to fully understand refspecs. Once people do, they tend to understand all essential ramifications of branch and repository naming.
All it does is add this simple check before actually pushing:
if (remote_ref("blah") != local_ref("remote/blah"))
fail();
Most of the time it doesn't matter, and for most people's uses of --force it would have no effect (because most people are just pushing to a branch they're the only one pushing to). But every now and then it helps a lot to avoid losing data.I try to be pragmatic about this sort of thing yet push —force is one of those cultural no-no’s for me.
git init --submodule --recursive
Or is it
git submodule --init --recursive?
God I hate this UX so much I usually have a ./fetch-subrepos.sh that runs a bunch of "git clone" commands.
And if I push without first pulling, must it always punish me with a merge commit? Can't I say "oh shit I don't want to do this, go back and git pull"?
This is a source of probably 50% of my "ah, fuck, time to undo..." moments with git, these days. I hate that shit. Muscle-memory gets ahead of me and I commit on a shared remote branch, which would be fine given our workflow except that I didn't pull first. What a pain in the ass.
[pull]
ff = only
If it does fail I can decide whether to merge or rebase.I would guess there's an easy way to make git do this automatically for you via config so you never forget, but I just never, ever `git pull`
Or:
> git config --global alias.up '!git fetch && git rebase --autostash FETCH_HEAD'
From:
You probably also want:
git config --global rebase.autostash true
I agree with having as many commits as possible be compilable, but that's not the sole criterion, because there's a tension between that and having granular history: if you squash the whole history of the repo into a single commit then that means 100% of commits are compilable, but it's still a bad move. Conversely, a non-compiling commit in between two compiling commits is not a big problem (you just make sure your git bisect script skips non-compiling commits) - what really matters is keeping the diff between two successive compiling commits as small as possible. IME the best way to achieve that is never rewriting history.
I think I know git well, but you got me confused. I've never heard of pushes causing merges. Surely you are talking about pulls, right?
The only theory that makes sense is that this person doesn't know how to `pull --rebase`, but the order of `push` vs `pull` wouldn't change the presence of merge commits, so I'm still confused.
If I pull from origin before making my changes, I don’t have to merge, obviously.
But correct me if I’m wrong: I think that if I don’t pull first, but my changes don’t conflict with any part of what was done by the previous commit(s) I missed, I’ll still have to merge if I touched a file they touched.
This is a common scenario for me. Correct some typos in comments for example, and I get forced to figure out how to merge using vim, which I don’t know how to use at all (being a nano user). I’m sure I could and should switch to at least using nano by default, but I don’t know how merging really works, either.
What I really want to do is undo my commit, pull, and redo my commit. Then I don’t have to figure out git merge.
I do understand the problem being discussed; what I don't understand is what it has to do with pushing first. You have the same problem no matter which order you use `git push` vs `git pull`.
> I think that if I don’t pull first, but my changes don’t conflict with any part of what was done by the previous commit(s) I missed, I’ll still have to merge if I touched a file they touched.
Yes, that's true.
> What I really want to do is undo my commit, pull, and redo my commit. Then I don’t have to figure out git merge.
You can do that with `git pull --rebase`, which, as others have mentioned, you can set as the default behavior of `git pull` like this:
1. After making a PR, there are conflicts when merging into main. In a merge-based workflow, I would merge main into the feature branch, resolve any conflicts, then push. In a rebase-based workflow, I rebase the branch onto main, resolve any conflicts, but now I need to push --force. As some of the other comments have mentioned, this can be improved with --force-with-lease, but still isn't the greatest.
2. After making a PR, there are some typos that need to be fixed. Fix these in an interactive rebase, to edit the same commit that introduced the typos. Also requires either --force or --force-with-lease.
3. When the PR is accepted, the result is rebased on top of main. My local branch still exists, and must be deleted. I would prefer to use `git branch -d` to delete the feature branch, but this rightfully says that the feature branch hasn't been merged in. I instead need to use `git branch -D` to forcefully delete it, introducing a point of human error. (There are some cases where git can delete the branch safely, which I think occurs either when the feature branch has only a single commit, or when the feature branch can be applied on top of main without a rebase, but I haven't exactly determined it.)
#1 and #3 are cases where a safer option cannot be used due to a rebase-workflow. #2 would exist in either case, since even in a merge workflow, rebasing of branches before they are pulled makes sense to do.
FWIW: it occurs when the feature branch was based on the tip of master (because no-one else has committed to master since you branched/since you rebased onto master) - in this case rebasing your feature branch onto master is a no-op and the commits that go into master have the same hashes as they had on your feature branch.
Git submodule runs commands on submodules.
What is hard about this UX?
And it's not punishing you, its doing what you asked, to pull into a non matching head, how does it know you're not using git in the intended and distributed way?
Btw, just quit the editor without saving, it aborts.
Most people want a single source of truth workflow that corresponds to the old total ordering imposed by svn or p4.
On the contrary, you want those commits for bisection, which is the main reason to have a VCS history at all.
> Most people want a single source of truth workflow that corresponds to the old total ordering imposed by svn or p4.
People think they want that, but I've never seen a convincing case for why. Bisect works better if you use merge. Blame works better if you use merge. And if you really want to see the history without merges (why?), it's one flag to do that.
AFAIK whatever's opened does need to block the CLI, so you can't use a command that opens a GUI editor then returns immediately or git will interpret that as your having closed the file without saving, but otherwise any editor should work, CLI or GUI, and can be assigned in your git config.
What they're saying is: if you quit that editor without saving the commit message, git will abort.
For the rest, there's shell autocomplete and muscle memory.
I'm the guy people go to fix Git screw ups at my jobs but I just click a few buttons or drag a few commits...
Maybe you've already read it, but this is what let me grok the underlying data.
Comments like this, which points to a resource intended to help people "grok the underlying data", has the effect of seizing the focus of conversation and implicitly retargeting it to be concerned with with people who don't understand the underlying data model. When you been through this enough times, it just comes off as incredibly annoying and a source of tiresomeness.
Even worse, I'm not sure I correctly remembered the weird combination of actions and flags to use to get back to the state where I can continue with what I wanted to do in the first place.
That article is a good example of the problem. It tells me `git rebase` is an easy thing to do but I better not use that distributed VCS to publish my work that way, where 'publish' probably also applies to different machines of mine.
It's just that the command-line interface is very opaque regarding what it does to that data.
For instance, say I want to apply the last three commits I made in one branch to another branch. It's a very simple operation conceptually.
Good luck remembering that the command that does it is rebase, and what the arguments for it are.
1. Despite the claim that git never loses data, there are actually some dangerous operations that will irretrievably nuke your work with no warning. Git checkout is the canonical example. This makes me very gun-shy about doing anything that I'm not intimately familiar with.
2. Git's merge is not smart enough to realize that identical changes in two branches are not actually a conflict. I've often ended up in situations where a small bug has been fixed in two branches which then won't merge without manual intervention. This is incredibly annoying. (To be fair, this is not unique to git. But because git encourages branching more than other systems, I encounter it more when using git in idiomatic ways.)
3. This is the biggie: translating the abstract idea of what I want to do into an actual git command is a black art. The underlying model is beautiful, but the UI is atrocious. The plumbing is great. The porcelain is cracked and mildewy. There are mysterious valves and pipes all over the place when all I want is one control for the hot water and another one for the cold.
Contrast that with TFVC which tries its best to nuke everything within it's reach (that is not under version control) in inexplicably stupid ways.
tf reconcile . -r -clean
This command is equivalent to git reset --hard origin/head && git clean -fdx
Because that's sensible. tf reconcile . -r -i clean
One might think -i is a short flag for -ignore. No. It's short for -preview. That's a "feature". Good luck finding the documentation for this idiotic behavior.How about if you pipe a list of files into tf reconcile, to avoid it's idiotic behavior? Say
git ls-tree HEAD -z | xargs -0 tf reconcile -r -clean
You better hope that ls-tree outputs something, otherwise that is the same as calling rm -rf .
I can't say I recognize your experience. You only risk creating a mess of changes to the point where it's hard to recover due to the share amount data it hasn't deleted.It's the same reason consumer operating systems have a trash can and undo features. Just railing on people with "you should've known better" doesn't really help.
The manufacturer of the machine won't be responsible for sure if you didn't even read the manual… Clear case.
That's reality in engineering.
If developers want to call themself "engineers" they should behave as such.
On the other side you don't let people without special training even close to industrial machines! The risk they could get killed by an accident is just to high.
Here lies the discrepancy actually: "Software industry" is as much an industry as running a kindergarten is. Also there is the most time no "engineering" at all in "software engineering"… Just trail and error until "something works" (or at least looks on the surface like it would, no matter how broken it is on the inside).
As long as people are supposed to "learn on the job", and there are no clear quality / security standards in this "industry", and stuff is called "engineering" even no true engineering approaches are followed, nothing will change. Machines will continue to kill people in completely avoidable accidents (virtually)…
But that's in my opinion a fully homemade misery actually.
It is not a different league it is a different sport. If a factory was designed with the equivalent of gits usabilty today, it wouldn't even get a permission to be built based on the plans alone. Also the person who planned it would have a hard time ever doing so again.
If git was a bridge it would have no handrails and it would oscillate in certain winds and for some odd reason there is a roundabout in the middle.
It is still better than no bridge for sure. As one of the first of its kind a lot can be excused, but fundamentally better engineering is possible as well.
The only single-operator machine I can think of which has an interface even 1/10th as complex as git is an automobile. The number of fatal accidents that could be remedied by reading the manual is vanishingly small.
Documenting poor design rather than improving it is lazy, bad engineering. Requiring rote learning of an very complex interface that poorly represents a moderately complex data structure is lazy, bad engineering. Macho, hyperbolic, gatekeeping arguments for non-design as a design philosophy is advocating for bad engineering. Being mad that people want products with interfaces that make sense is being mad that people want an important facet of good engineering.
We are discussing designing the machine in a way that removes these options altogether.
This is objectively and obviously a better strategy because humans are fallible, even when well trained, and the time put into safety training can now be put into an actually productive direction.
You're working from the perspective that machines and git must be dangerous to be effective. This is an assumption that should be challenged before the enormous waste of safety training is accepted.
When I started web dev, I earned $15/hr. Beat the $8/hr I was making before that in landscaping. Now I make a little more than that, which still isn't much. My clients/employers don't pay crazy wages and they don't expect crazy quality work. They know they get what they pay for, and it works out for both parties.
Maybe's OK to have shitty, mediocre code for 90% of the world's needs... a small biz website, with the ecommerce/PCI bits outsourced? Sure, why not. It mostly works, and if it goes down for a few hours a year, maybe that doesn't meet super-reliability standards (can we count 8s instead of 9s?) but it gets the job done well enough? Shrug.
Sure, proper engineer techniques matter for certain applications. I would never want to touch industrial machines, or medical, or space, or automobiles... anything that could blow up and/or kill someone. But most code out there is just for some local, small-scale use, mostly temporary anyhow and bound to obsolete in a few years if not months. There will always be mediocre businesses needing mediocre devs for mediocre pay, just as there will be elite enterprises that require the world's smartest people.
Problem is, git was designed by the super smart for the super smart, great engineering with terrible UX. And it was kinda just trickled down to the rest of us, and it feels a bit like trying to teach Mom to use DOS and edit config.sys just to play a game. Now consumer software UX has leaped forward by decades and it shows, but a lot of the command-line dev tools are still incredibly arcane. They don't have to be, but it's not a priority to fix/improve their UX because, I suppose, it's engineers who are proud of their engineering, not proud of their ability to dumb it down for the rest of us. I don't blame them, I just know I can't meaningfully contribute to git (the project) because I'm not smart enough, well trained enough, whatever, and that complaints would fall on deaf ears like yours. It's an altogether different culture. Elitist by design, or meritocratic if you will.
And believe or not, I've probably spent more time learning git -- reading documentation, diagramming it out with coworkers, cloning repos and experimenting with commands, etc., following a shit-ton of tutorials -- than any other skill I've ever had to learn. It was quicker to learn Perl and regex than mid-level git.
If you want to pay for the world's would-be engineers to receive all the training to go from mere dev to proper "software engineers", by all means please do. But otherwise, well... you know what? World's gonna keep producing mediocrity. Most of us are just average.
Also, git reset ---hard HEAD^ deletes nothing. The commit HEAD was pointing is not deleted. You have to work really hard to delete that commit accidentally
If you type in "rm -rf ." there will also be no "warning" about what happens next…
I for my part don't like systems that after giving it a command very explicitly asks me whether it should really execute that command. "You just pressed the button to delete those files. Do you really want to delete those files?" "Oh, no sorry. I'm the operator of this system but I press buttons randomly just for fun. Actually I have no clue what I'm doing. Thanks for that pointer! Please ask me also the next time, as I'm not going to remember what this button does. In fact I have no clue at all what I'm doing." Dude…
Git is not a backup tool. (Even you could use it as such).
If you need a backup, make a backup.
I'm aware of the fact this this comment may sound impolite to some. But out of my perspective it's a feature and not a bug that unixy systems traditionally don't ask stupid questions after you told them to do something. It's an attitude of respect towards the operator: The system assumes that the operator knows what he is doing. Asking questions in the style of "do you really, really want me to do what you just said" is on the other hands side just extremely rude against the operator—as the systems assumes the persons in front of the keyboard does not know what he is doing at all! I hate systems with build in "support wheels". The creators of such systems obviously thinks their users are cretins, and that's very annoying.
Well, that's the purpose or "reset --hard". "reset" alone doesn't do that. That's the difference with "rm". With "rm" you expect something to be removed. "rm" may fail because you gave it a directory but you forgot "-r". Or it may fail because you don't have the right to the parent directory or something. But it is pretty much expected that something will be deleted if you run "rm".
From the perspective of people who don't understand the difference between "git reset", "git reset --hard", etc., that some of those will destroy their data but not others, is part of the confusion. And when they picked one instead of the other and lose their modifications, they'll blame git from being confusing to use, not themselves for not doing a backup before running a command in their source directory with a program that is used to manage source directories.
By the way, have you ever run across a situation where the standardized behaviour of rm without -f (to wit, check the permissions of each non-directory, and, if it's not writable, prompt if stdin is a terminal)?
it'd be trivial to modify that behavior to refuse to continue in such a case if you want double seatbelts
also, if the changes were staged they were recorded in the database and remain there
I think this is part of a downside of rebase-centric workflows, since it encourages making multiple branches with identical changes, but no shared history. At some point I want to read more pros/cons on different workflows. My current thinking is that rebase-only sacrifices far too much on the altar of a clean-but-inaccurate commit history, but I don't yet know enough to say for certain.
https://www.git-scm.com/book/en/v2/Git-Tools-Rerere
If you have a lot of long-lived branches that don't merge and get deleted you have a whole different world of pain from normal git usage, and need some resources designed as SCM experts to manage this stuff. But really short lived branches that deliver net steps forward are the way to live.
I hate branches because it breaks git-bisect. Linux kernel history has lived without it, so I'm guessing the "our code is so complex we need it" is a false idea.
Also, the actual history of what main/master points to is linear. If the bit-post merge are identical, it doesn't matter if they were rebased or not except having 2 ancestors for HEAD is a lie.
Those guys are still sending commits (patches) to each other by mail, I think they willingly live in such a special and weird bubble that it just can't be compared to any "real life industry project".
We've recently started to enforce linear history on our main branches at work, because it makes analyzing broken builds such a breeze. The mantra is "Insertion is hard, analyzing/understanding is easy" (for a merge-based history, it's pretty much the other way around), and so far it's quite the success.
It did take quite a bit of effort though to get everyone up to speed, and it still requires the occasional help to clean up the commit history of some merge request, but it was well-worth the trouble so far.
Can you explain what you mean by git merge breaking bisect? I've never run into that problem at all. And sure, it might bounce between the branches as you bisect, it still identifies the commit that introduced a bug. The only issue is if one of the commits on the branch fails to compile/test, which is poor commit hygiene, but can be excluded from the bisect.
I still need to do more research, but I think I'd lean toward having a merge workflow, but with no fast-forward commits. That leaves a clean history on main with --first-parent, but still leaves the details available in the branches as needed.
The similar project [Jujube](https://github.com/martinvonz/jj) has experimented with backing up even unstaged changes after every command, and apparently it works well for them, so we could do the same in the above design.
Undoing even untracked changes might be a bit much.
Take branching, which is something you're supposed to be doing all the time. So abstractly, I want to be able to get a list of my current branches, create a new branch, check out an existing branch, delete an existing branch, and maybe rename a branch. I would expect the commands for these operations to be something like:
git branch list
git branch create [name]
git branch delete [name]
git branch rename [old-name] [new-name]
We can argue over whether checking out a branch should be "git branch checkout [name]" or just "git checkout [name]", but in either case, if I have unsaved changes in my working directory, I would expect to at least get a warning about this before that work got clobbered.None of these things are actually the case. Git checkout will clobber unsaved changes in my working directory. "Git branch list" is just "git branch". "git branch create" is "git branch [name]". So creating a branch is the SAME COMMAND as the one you use to produce a list of current branches, just with an argument. Madness. And this is the rule, not the exception. Minor variations on a theme can result in radically different commands with various mysterious arguments. There is no rhyme or reason or regularity. The only way to know is to look it up.
I've been using Git professionally for of a decade and you just taught me something new. I always use `git checkout -b [name]` (which is a completely bananas UX, but it's the one I know).
As a counter-opinion, I actually like it this way. Especially since it's the rule and not the exception. When one makes the small effort to learn the commands, the payoff in saved keystrokes from avoiding typing the redundant pieces is great.
As a painfully simplified justification: Directory listing is "ls" and removing a directory is "rmdir" - I much prefer that to typing a hypothetical "directory list" and "directory delete", even if the later fits some nice consistent shape.
It's one of the reasons I hate powershell/windows-style CLI and its 60-character long arguments for every function.
I agree with your other criticisms, but IME git's branching-heavy approach makes this one easier to avoid: it's natural and easy to create a separate branch for that bugfix and then merge it everywhere that it's wanted. Or to just merge your colleague's feature branch that has the fix on into your own feature branch, if you know their branch doesn't contain any dangerous changes / will hit master ahead of yours.
(If the diff is actually bit-for-bit identical I think it doesn't conflict? But obviously that doesn't happen so much in practice. git merge -Xignore-all-space can be helpful in some cases).
There is if you discover the typo in the middle of working on something else, which is usually the case for me.
Why/how? If I had other uncommitted changes when I found the typo I'd stash or commit them so that I had a clean checkout to do the typo fix from, but can't think of any case where cherry pick is the right answer.
1. Switch from your editor to a shell
2. Stash
3. Switch from your shell back to your editor
4. Revert the editor buffer
5. Fix the typo
6. Save
7. Switch back to your shell
8. Commit
Now you have to push that fix into all the upstream branches (because the whole point of this exercise is to avoid merge commits from someone else fixing the same typo), so:
9. Checkout
10. Pull
11. Merge the typo fix (at which point you might discover that someone else has already fixed this typo, or that you have an actual conflict that needs to be dealt with)
12. Commit
13. Push (at which point you might discover that someone else was in the process of fixing the same typo and just happened to push before you did. This is unlikely, but it becomes more likely the longer you wait between step 10 and 11 because there is a race condition here.)
14. Repeat for every upstream branch in which anyone could have already fixed the same typo
Then, finally:
N. Stash pop
N+1. Switch back to your editor
N+2. Revert your editor buffer
N+3. Try to remember what you were working on before you started this process.
It's a hell of a lot easier to just fix the typo and say, "ah, fuck it", and then curse at git for not being smart enough to figure out that identical changes aren't conflicts. ;-)
Well, I think it's very much worth having git support integrated into your editing tool, so for me the workflow is more:
1. Shelve current changes, if any
2. (Optional) fetch origin
3. Switch branch
4. Make the fix
5. Commit-push
6. Merge this branch upstream
7. Switch branch back
8. Pull upstream master
9. Unshelve if necessary
I wouldn't consider it my responsibility to apply the fix to anyone else's branch - rather every branch has an owner and it's their responsibility to update from origin master at whatever frequency suits them - and if someone else has fixed the problem in parallel then either I notice at stage 4, or, if they did it in between me reaching 2 and me reaching 6 then 6 fails harmlessly (I just continue to 7 and 8 and I pick up their version of the fix, no need to resolve any conflict). This does rely on having a single shared "origin master" (whether that's your git hosting system, your release manager's machine, or something else) that can do step 6 atomically - if you really want a 100% decentralised system then yeah the price of that is eventual consistency.
All that said, git merge absolutely does treat a byte-for-byte identical change as not a conflict (I just confirmed with git merge-file), and will treat an identical-except-for-whitespace fix as not a conflict if you pass it the appropriate flags, so I think something else must be going on to cause the problem you're seeing.
I guess my situation is a bit unusual. We have two production branches because we're running our code in two different environments. So we have two branches that are effectively origin/masters.
> git merge absolutely does treat a byte-for-byte identical change as not a conflict
That's news to me. I guess I'll have to try that again because I have a very clear memory of seeing this happen and thinking WTF? But that could have been a long time ago.
You might enjoy using Magit.
I definitely agree. The UI is terrible. But it is learnable.
I would recommend using a GUI initially so you can learn what the operations are and do without having to figure out that it's not `git remote list` it's `git remote -v`; it's not `git clean` it's `git checkout . && git clean -fdx` and a million other paper cuts.
Between my colleagues, there is a strong correlation between using a GUI, and messing up the repository.
I agree that Git's UX is pretty bad (checkout overloading; overlapping between checkout and reset; push overloading... yikes!), however, I believe that in contexts where using Git is a constraint, stop using GUIs is the best strategy one can apply to improve the understanding.
First of all, a well-designed Git GUI (examples below) exposes the git's underlying data model to you; in contrast, the CLI obscures it until something breaks and you're forced into it without context. Git operates on a graph and there is simply no way around it. The more you're exposed to the graph, the better you can mentally model it and ask it to do the right things.
While there are many half-baked Electron-based UIs that only unnecessarily complicates things, there are good ones too:
On Windows (and Linux thru Mono), I use GitExtensions. The visualizations are sane and discover-friendly. It will tell you what to expect.
When I work on C++ projects, the one built into CLion (JetBrains family; as mentioned by a sibling comment) is very good on its own. What impresses me the most is that has good visual support of a patch-oriented workflow. You can work with multiple "changelists" offline, shuffle individual changes around, seamlessly convert between changelists and patch files, and (most importantly) still work nice with the vanilla git model as the "actual history". It also works transparently with conceptually monorepo projects that have multiple physical repos, allowing you to do simultaneous commits.
I feel VSCode, GitHub client, Kraken, and several other Electron-based stuff, are too focused on the "polish" than substance, or are too opinionated to be used across repos I don't own.
All GUIs are not created equal. The JetBRains GUIs are pretty good. Others try and impose git "their way" and just invite creating a mess.
Basically the IDE has its own revision history of your project’s files that it stores and you can recover your files from it. So when you accidentally revert instead of undo or use —-hard when you didn’t really mean to, Local History comes and saves the day.
Git is really weird but useful.
[core]
editor = vimFor example in ubuntu you can do
sudo update-alternatives --config editor
Some programs will use the environment variable $EDITOR, so you can add this to your shell startup configs export EDITOR=vi
Or specifically for git cli you can run git config --global core.editor viEDIT: Also, I really like being able to look at a diff of every file before I commit, and easily choosing which files to include in a commit. Too often I see people on the CLI accidentally committing changes they didn't mean to, because there is no easy way to check everything at the last minute.
git commit -v
Displays a diff at the bottom of the editor that pops up to write a commit message. git config commit.verbose true
to always have this behaviorThere's a user request that's been sitting around for a while to pull it out into a dedicated application, which I could personally get behind since we have one project at work that's pretty difficult to run outside of Eclipse
Then there's 'git add -i' to easily choose which files to add
Not GP, but I find it extremely hard to parse visually even after having had to do it a lot. Something like git-delta makes it bearable, but a decent syntax-highlighted, color-coded, side-by-side diff view with proper keyboard navigation (next diff, next file) is a huge quality of life boost for me.
> Then there's 'git add -i' to easily choose which files to add
Didn't know that, just tried it, and, wow, that's easily the most unintuitive and inscrutable TUI since vi. I haven't been able to make it show a simple diff; someone else also tried, failed as well.
git diff
git diff --staged
git restore --staged --partial
git commit
you can also add commit hooks to reject changes like conflict markersAnd it's time well-spent in my opinion; git will probably be one of the longer-lasting constants in software development.
Unfortunely.
It's something I use all day, every day. I'd say it's worthwhile to learn if your daily job involves working under source control
Recloning to me is a bit like tearing your house down and rebuilding it just because you painted your living room the wrong colour (in most cases!)
Sure, recloning and cp your changes to a fresh local repo will _work_, but what happens on the day you don't have access to the remote? What happens when you have to work on a repo that takes minutes to clone from the remote? All of those lost minutes because you don't understand how to use your tools means you aren't getting work done.
> memorize an obscure set of incantations like some kind of D&D wizard
If you're using Git then you're probably someone that works on software, isn't our whole job memorizing and reproducing permutations of obscure incantations to produce business value? Knowing how to use your tools provides business value, that's not some hot take but literally how you provide value to whoever pays you.
Same for VSCode on non-Windows.
I only mention this because I came to hate Sourcetree in its newer iterations on Windows so much that I tried out Visual Studio's support and was pleasantly surprised.
I do most of my git through one or another UI (right now, mainly the integrated functionality in VSCode, a gitflow workflow extension for VSCode, and repository history graph extension for VSCode that supports doing operations against branches/tags/commits from the graph.) I feel loke I’m thriving that way.
Its not a substitute for knowing git, though, and I think that people who lean on a UI as a substitute for knowing what is going on underneath rather than as a convenience layer are not likely to thrive.
There are days where I daydream of Clearcase when dealing with git.
If you wrote git 2.whatever from scratch would you structure rhe commands a bit differently? Yeah, sure, probably; but I always think these threads are way overblown. The common stuff that you use frequently.. well you use it frequently, so either you remember it as a result or you use the alias feature so that you can. For the less common stuff.. if you have to look it up in the excellent documentation, is that a failing?
Yes, if we ever want version control to become mainstream.
Version control is a very practical, day to day concept.
It's just almost unusable for regular folks.
And regular folks for sure won't use CLIs (unless you point a gun to their heads).
Who's trying to make 'regular folks' use git, with or without a gun?
I would have loved to have that option quite a few times. Lots of projects that involve software, but also get contributions from non-developers, or need almost no user-facing config, but not exactly zero. If git wasn't so hostile, quite a few tiny config web UIs I've built over the years could have been SublimeText or the like and a very userfriendly git client. It's kind of frustrating to have a repository with a directory full of .sql files, a CI pipeline that versions and verifies those and deploys the updated app, but the domain expert who is perfectly capable of writing the SQL still needs me to put the file in the right place and commit+push it, because they (understandably) just won't use git on Windows.
I'm not sure how understandable that is, especially in the age of WSL - but I just won't use Windows so what do I know :)
But why is one popular when the other one is so much better? I guess we will never know
Having used Bitbucket and GitHub, I think GH is much nicer - both in the sense of basic stuff like page loads being faster, and in terms of features. And since it's the main tool that everyone on the development team spends their time on for communication & collaboration, those things really matter.
But you do know how to pronounce "Unnecessarily"?
This happens all the time
Mercurial is less intimidating if you don't know much about internals. But tbh, when I use Mercurial I still find myself searching which command to do things. As a frequent power user, I find the Git CLI to be more useable.
I once knew how it worked with moderate level of detail, but I simply do not need anything advanced for my day-to-day work.
Usually my response is if you use something daily, and you know you lack the skills to use it effectively, why don't you improve your knowledge and seek out training or education?
Do you treat a new programming language or framework with the same disdain?
I know I'm gonna get some hate for pointing this out but this same disdain is what causes things like the branchless workflow. A workflow that hamstrings yourself to git stash as a poor man's branch. I get it, I used that as crutch for years, then I spent some time learning git properly.
Most people haven't even watched the one hour talk where Linus talks about the design and building of git.
One hour might sound like a lot to understand why branching is so amazing, and why distributed source control is hard but it's a tool I've used for almost a decade and won't stop using for the next decade. I think it was worth it.
I have no memory of any of that beyond the basics now. Git is like that. There is no way I will remember commands I only use once a quarter or so when something goes wrong.
Maybe git isn't the right tool for you?
I have a set of commands I use multiple times a day, for everything else there are manuals and docs to reference.
Git branch, commit, rebase, merge, clone, check out, pull, push, submodule, remote, and maybe a couple more, are there specific commands you don't use daily? Other than remote and submodule I use all of those almost daily.
Git is highly professionalized. It has layers of modal state. That is built into the operating model. It is made for Linus Torvalds, a professional merger of code. If you are using all of those commands "almost daily", you are a professional code-merger too.
And again, maybe git is the wrong tool for you, that was kind of my point, use cvs or svn it's much closer to what you seem to want.
Some sort of basic version control is definitely important.
> Usually my response is if you use something daily, and you know you lack the skills to use it effectively, why don't you improve your knowledge and seek out training or education?
Seeking out that training would be useless since I don't use git enough.
> Maybe git isn't the right tool for you?
Git is definitely the tool for me. It's better than any other available tool for my situation.
Why the hell would I branch more? Just to get better at git? Sorry, I'm sorry that I work on a small team! Maybe I should go back to CVS?
Stop gate keeping.
I have multiple branches on the go in a one-person project. I find that very useful. But if you don't, maybe you should go back to SVN? Using git without merging between branches seems like you're making your life complicated for no gain - like using a distributed system framework to run a single-node system.
The comment I'm replying to said "branch more" as if that would solve some kind of problem with edge case complexity.
You don't want branching and you still think git is the tool for you? Compared to say svn?
Honestly, use svn, its the better tool for your use case.
You don't have to use git.
Don't gaslight me by saying you don't need gits main feature and then suggest I'm gstekeeping, I'm not, youre welcome to use git, just don't complain about your lack of ability to RTFM.
I use a modified gitflow. The article and this thread are about how when you get into trouble, git quickly becomes complicated. It’s not about branching being too hard or whatever you turned this conversation into in your head.
FYI, the branchless workflow linked to in the post is really the opposite of this. It encourages making commits even more often than you would in the traditional branching workflow, and discourages using the staging area or stashes when commits would work fine.
`git reflog` shows the commits where your current branch pointer has been.
`git reset --hard` moves your current branch pointer and to the given commit, then modifies your working directory to match that commit. `--soft` moves the branch pointer and does not modify your working directory. `--mixed` moves the branch pointer, does not modify your working directory, but does clear staging.
This is supposed to be covered in `man git reflog` and `man git reset --hard`. I admit that it could be more readable though. Currently, it's more of a technically-correct introduction than a layman's. I guess it's really more of a documentation for experts. Some have made fun of this: https://git-man-page-generator.lokaltog.net/
In layman's terms, `git reflog` is the history of the positions you were at: you'll see every commit you've visited recently, so as long as something was committed, you won't lose it. It's here in case you lose some commit identifier (for instance you finished rebasing but are not happy with the result: the branch now points to the sad commit. Grab the reflog, copy the commit identifier and reset the branch to point it to where it was before).
And `git reset --hard`... `git reset` changes the branch "tip" (pointer) to another commit: the commit tree always exists. Branches are "named commits". `git reset` moves these tags around. The `--hard` part "just" replaces the entire content of your working directory (including non-committed changes) to the commit you give as a parameter. With no parameters, it just resets to the latest commit in that branch, so it's like saying "clean my working directory back to what was committed, discard my changes". Perfect for losing work.
I agree that git "porcelain" commands are sometimes ill-named and a bit counter-intuitive to grasp. For me, learning git paid of (sort of: I don't spend time fighting my issues, I spend it helping others fix theirs).
I'm keeping an eye on better-designed alternatives like pijul. mercurial is interesting and has better-named command, but I sunk some time learning git already, and know it better, so hg has very little more to offer to me.
[1]: https://mirrors.edge.kernel.org/pub/software/scm/git/docs/us...
Most people don't know how the Internet works and yet it's widely used.
You don't need to understand the inner workings of git. You just need to know some commands and some basic concepts.
Yeah. I've met many developers who have no idea what a cookie even is, people who have never read a single IETF RFC.
It is a tool. One should not need to understand the inner workings of a tool to use it. How many APIs do we use where knowing how it does what it does is required? Whether it's Stripe or Node or Bundler or ..., the user does not have to know what happens inside. The need for incantations makes some people feel powerful or exclusive. I just want my tools to do their job so I can focus on doing mine.
If you have ever done a "git reset" (which implies --soft), then you should know what "hard" in this case means. I am not sure, I knew that it would most likely get rid of my uncommitted changes. I "git stash", then "git stash pop".
Do not run anything without knowing what it does, you should consult the manual page: "man git reset". Search for "--hard", and you get:
--hard
Resets the index and working tree. Any changes to tracked files in the working tree since <commit> are discarded.
I do not find myself reading the manual pages that often anymore. That said, I do have my own notes which I read sometimes, just to be sure.Slightly off-topic, but I love working with meld! If I do a "git rebase --onto [...]" I do not get "meld" open automatically, you gotta type "git mergetool" to resolve conflicts.
I wonder if it is possible for someone to write a meta-CLI that works on top of git like “git for humans”. While, still leaving the power for expert users
You just don't need to learn unless you really require it. And even if you do learn you will eventually forget if you don't use all the features.
Its not hard to see why people don't even bother.
FYI: `git reflog` saves your ass when you accidentally delete a branch locally, only to then realize there were valuable commits yet to be pushed to master! How it does it is another matter.
Systems enabling high usefulness with little education (i.e. shallow learning curve, at least initially) and then incremental education are ideal. This isn't essential, just desirable. Git can err a little on the steep learning curve/mystery black box side of things a bit. But it is still a damn fine tool.
The truth is, while we use git every day, most people really don't understand how it works.
There I said it. And I'm not ashamed.
I don't really know how Git works. And I think I'm not the only one.
What does "git reflog" or "git reset --hard ...." do? What are the implications?
We don't really know.
I feel stupid. But hey, at least I'm honest.