Maybe my workflow is a lot easier than others, but I work on a team of 10+ engineers and my Git usage is dead simple. I do everything from a GUI (Fork for macOS) and very rarely have to deal with any complicated issues that require a terminal.
- Always pull w/ rebase for the current branch.
- Always merge other branches into your current branch, eg master -> feature.
- Always stage individual chunks of code one at a time to make sure I'm committing the right stuff.
- Always squash feature branches into a single commit when merging back.
- Stash changes if needed when switching branches.
- Cherry-pick one-off commits if needed.
- Append a previous commit that I haven't pushed yet if I happened to forget something.
- For complicated merge conflicts I switch to Visual Studio Code which also has a great GUI.
I think this covers 99% of the stuff I encounter in my day-to-day.
I agree with basically everything you wrote except I’ve had much better experience rebasing my feature onto origin/master instead of merging master into feature. Keeps the [branch] history cleaner.
I hate merges. "Merged ... into ..." is such an uninformative message! Some codebases are filled with those. I prefer seeing each individual commit messages, so... rebasing all the way for me! One problem I have with this is that the committer date of all commits become identical instead of retaining their original date aligned with the author date (which I would like to be the committer date). Is there a way to workaround this or fix it in a "non-dirty" way? It might be the case that I am doing something wrong, if so, what am I doing wrong exactly, or how should I go about it?
Merge flows are vital for understanding the history after the fact. It should be trivial to make a view that just hides merge commits, if you're so allergic to them, but you can't recover them once they're gone.
For the sake of your future sanity (and your coworkers'), please stop trying to rewrite history for the sake of aesthetics. The defaults are what they are for a reason.
It is not for the sake of aesthetics though. When I do, say, git pull remote-foo master, then it opens up my text editor with the default "Merge branch 'master' of git://.../foo into bar" commit message. How is this useful or informative? There are lots of repositories with this uninformative commit message. Yes, perhaps rebase is not the best way to go about it, but then could it just be a limitation of git? Or am I missing something? I am not sure. I am looking forward for https://pijul.org/ though, it seems to solve this issue.
Additionally, if I do the aforementioned git pull, I have no way of turning the commit message into an informative one because I have no information regarding the commits. Is there a way to actually know it beforehand to turn that message into a meaningful one? If so, how would you do it? If it is possible, should I just have a list of all the commits' message in that one merge commit? Regardless, I see lots of "Merge branch ... of ... into ..." commit messages and they are not useful to me at all. Why do you think people keep doing it?
It's much more informative than not having any information about where or whether a merge happened at all, regardless of what the message is.
> Additionally, if I do the aforementioned git pull, I have no way of turning the commit message into an informative one because I have no information regarding the commits. Is there a way to actually know it beforehand to turn that message into a meaningful one?
If there are any conflicts then it will force you to stop and resolve them, which seems like a decent point to document anything that needs to be. Otherwise, you can always amend in a more relevant message afterwards.
> If there are any conflicts then it will force you to stop and resolve them
Rebasing does this too. Can't we compromise, and agree to never allow "git pull" to spontaneously and unthinkingly create a merge commit with two parents?
Instead, let's do merges of our rebased feature branches, into the trunks that they are based on, but with "no-ff" – this retains the information you wanted about what feature branches have merged into what trunks, and when. But the history of each branch maintains linearity and no commit ever has more than one parent.
I don't have to work with either of you, but this is how we resolved this debate on my team, and we haven't looked back. It's not even worth having this conversation unless you need branches to be long-lived. But if someone else ever has to rebase your branch with merges in it, they'll thank you if you have honored this, and curse you under their breath if you have left these thoughtless merge commits around.
Do merge, but please only merge deliberately. (I also recommend checking out Deliberate Git for a great talk on Git, with similar ideas! https://vimeo.com/72762735)
> Rebasing does this too. Can't we compromise, and agree to never allow "git pull" to spontaneously and unthinkingly create a merge commit with two parents?
No. Shorter-lived branches naturally bring a smaller window to create nonsense history, but the risk is still there, and the benefits are as absent as ever.
> But if someone else ever has to rebase your branch with merges in it, they'll thank you if you have honored this, and curse you under their breath if you have left these thoughtless merge commits around.
So.. don't? That seems like a good thing, software should discourage you from shooting yourself in the foot.
Making a commit with two parents is shooting yourself in the foot, because every other kind of commit can be summarized as a single diff, which means you can definitely do bisect without misery.
What is the simple diff content of a merge commit which solves a conflict between two parents? There isn't one, change my mind.
We both agree that software should discourage shooting yourself in the foot. We disagree about which one of us is using the feature that encapsulates the moment where shooting ourself in the foot happens. It's not nonsense history if it reflects the order that branches were actually merged, which is far more interesting than the order in which commits were written concurrently across disparate branches IMHO.
If you are working with other people that can't read your thoughts, especially those who are too distant to reach you to return the favor when you have inflicted pain on them, they will appreciate it if you rebase your feature branch before submitting your PR and don't send forward an unmerged conflict unless we actually need to talk it over for some reason.
On the other hand I don't know anyone who will be mad that you did this. The rebase feature exists for a reason, and so does amend, reset, squash, etc. How many of these features do you think I shouldn't use? I am only discouraging the use of one feature, the automatic octopus merge that happens when you thoughtlessly run "git pull".
Why do thoughtless things? Don't you want to review the code you've pulled?
Rebasing also guarantees that the commit your CI has tested is identical to the commit that becomes head after the branches merge. How do you guarantee that your branches still function properly after merges? Carefully running the tests by hand between merge and push? My test suite is too large, and we are too busy for that, friend.
I think the fundamental disagreement here is about whether commits represent diffs/patches or snapshots of the entire file tree.
If you care about patches then the structure or order of the history doesn't really matter as long as the dependencies of each patch are satisfied (they produce the same final file when applied, and no conflicts). Take this philosophy to its extreme, and you end up with Darcs or Pijul.
If you care about tree snapshots then each commit is a promise that "I have made these changes, and afterwards the entire combined codebase still works and makes sense". If you alter the untouched code then this still breaks the promise, because "the entire combined codebase" is now different. Take this philosophy further and you end up closer to Mercurial.
Personally I lean hard towards the latter. Code tends to make a lot of assumptions about its surroundings that won't show up in a naive text-based diff (and thus won't generate conflicts), such as "class X exists and contains method Y".
The confusing thing about git is that it doesn't really make a clear choice. Some commands only make sense in a diff-based world (such as rebase and friends), while some only make sense if you think in snapshots (such as bisect, which tests whether your app satisfies some expectation at a given snapshot, not whether your diff does what it claims to).
And besides..
> Rebasing also guarantees that the commit your CI has tested is identical to the commit that becomes head after the branches merge. How do you guarantee that your branches still function properly after merges? Carefully running the tests by hand between merge and push? My test suite is too large, and we are too busy for that, friend.
If you care about bisecting then a rebase requires you to retest each commit in the branch, while a merge only creates one new commit to test. As a wise person once said:
> My test suite is too large, and we are too busy for that, friend.
> If you care about tree snapshots then each commit is a promise that "I have made these changes, and afterwards the entire combined codebase still works and makes sense"
This is an unreasonably high bar to reach for each commit. This is the right test to apply (among others) for whether a PR has the right stuff to get merged into a trunk, or not.
> What is the simple diff content of a merge commit which solves a conflict between two parents? There isn't one, change my mind.
Treat it as a squash where the individual commits can be recovered. Git treats commits as left-biased, so the branch you're merging into will be the primary parent.
I have to treat it as history now, because it's in my trunk. If it turns out to represent a test failure, then I don't want it in my trunk, but it's too late. Even though we waited for CI and only merged when the suite was green, we wound up with a failure in the trunk, because of the surprise merge conflict that got resolved after testing had already been performed. Neither parent head can be tested by itself to prove that the merge will pass all the tests. So unless you're pushing that feature branch up with the merge in it preemptively, before it merges back, then I don't know how your approach can fulfill this requirement. (If you do, then you'll wind up with twice as many merges, many of them with the shape of an octopus or multi-parent. IMHO this greatly hinders traceability and readability in the commit history, absolutely by enough to want to edit it out. Nobody is going to read that.)
I'm coming at this from my angle as a team lead, or mediator between novice contributors who are both pointing the finger at each other. If the two parent branches both passed, but the merge fails, and both upstream committers are gone, I no longer can have only one throat to choke. I have to check out both branches and confer with both contributors in order to resolve the conflict again.
One contributor or the other has broke the build. It will require additional investigation to decide which. If one branch has merged first, and the other second after rebase, then I never have this problem, and my debugging process has only one throat to choke. Delegation becomes much easier.
Whoever won the race to get their PR approved and merged first is not the one at fault. (This is the greatest incentive to get your stuff tested and merged, also!) The branch that has been rebased on the other one, then, must be at fault, because his merge came later. It's not a value judgement on a person, I am just keeping it simple. My one and only motivation for operating this way is to have 100% certainty that contributors' PRs which (we) have decided to merge after reviewing the test outcomes, are the same, so that all the intensive testing we performed and spent our time reviewing before the merges will not have been a waste.
I generally don't want merges in my feature branches, and I don't think you will convince me to want otherwise. But I appreciate you having engaged me in this debate. Every time I get to talk about my strongly held beliefs, I get a better perspective on why someone else might disagree. :+1:
My teams have usually been very small, and I can't say for sure that my strategy can scale. But we have scaled it successfully past 2 and into 4 developers. It requires everyone's cooperation, but once we have it this delegation subjectively seems about a thousand percent more reliable.
Rebasing and keeping commits makes very easy to read history, everything is in a logical order. The order may not be the exact chronological order in which is was written but that is rarely needded, the rebases guarantee that it _could_ have been written as the history shows and you can see all changes afterwards.
I have used this in projects and it worked great for the whole team for a long time. You can of course do something else which can work for you and your team.
If you're using rebase there's the `--committer-date-is-author-date` and `--ignore-date` flags. One uses the author date for both and the other uses the commit date for both.
Without using either flag rebase should update the commit date and preserve the author date.
If by rebase you meant GitHub's rebase merge option I think you're out of luck :-/
Hmm, it does? I was not aware of this. I did it twice (with two independent projects) earlier and it seemed to have retained the signature for some reason. It shows "Signature made ..." and "Good signature from ..." for all the commits I made earlier. I will pay more attention to it because those signatures matter a lot to me. Thank you for pointing it out, I will keep this in mind and make sure to double check.
I think what I said should be equivalent to what you do. When I merge my feature back to origin/master I do `git merge --squash myFeatureBranch` which automatically rebases everything and keeps a clean history.
This is really the only command I do by hand because I can't find the equivalent (yet, probably haven't looked hard enough) in Fork.
edit: oh I think I misread what you said. When working on a feature branch I don't care too much about the branch history since I know it's eventually being squashed back into a single commit.
Isn't this (or at least the end state) the same as rebasing myFeatureBranch from master before merging it in? I don't use Fork, but I assume it can do those 2 things with clicks.
Same thing here. If I'm working on a feature that I haven't pushed up yet I might squash my commits together for a cleaner history (it also helps with rebasing). If you ever mess up a rebase and finish it before you're able to abort you can use `git reflog` to get a reference back to the previous commit. It's come super handy a few times.
This likely works because a) you know what you're doing (cherry-pick), and b) you're being careful (staging carefully).
Anything works when both of those prerequisites are met. You're unlikely to do the bad things that force more advanced needs.
Everyone should do this. Think back to how much time you've lost due to git problems. It'd probably take less time to learn it correctly up-front, and never have to deal with them ever again.
Am I missing something, or is it impossible to, y'know, check out a previous revision using github desktop? Create a branch at a previous revision? Reset the branch to some previous commit? Any of these things?
It seems to have next to zero functionality. What is the point of it? What you you use it for, other than commit, push?
I know that, I'm just wondering what the point of GitHub Desktop is.
I use Sublime Merge, but no Git GUI I've used comes close to tortoisehg for mercurial. Tortoisehg is actually superior to and a comprehensive replacement for the command line. I wish something like it existed for git, if not for me then for the newbie-ish people I need to instruct on project workflows.
I agree. Almost every time I hear of someone having issues unpicking a complicated git issue it's because they were trying to be too smart with it and use every feature just because they are there.
Find a _simple_ git workflow, document it in a style guide, stick to it. Git doesn't have to be complicated.
I am similar but avoid rebasing entirely in favor of squashing. Squashing can also be used to turn a messy branch into a new branch full of nice clean independent commits.
I also don't bother appending (though my GUI lets me undo the last commit if I haven't pushed which is maybe the same thing.)
Most developers get way too fancy with Git and screw something up. I use GitHub Desktop and rarely touch the command line for Git. It has a great UI for being able to just pop over and see my current changes, and easily commit just a line or two from them to make commits other people can understand.
I will never understand why people use a UI for git. 99% of the time people who do that don't understand how git works. When something deviates from the normal workflow they struggle.
Never understand? I mostly use Git CLI, and it's a big pain. Weird terminology, needing to memorize branch names for easy use, differences between staged and pushed commits. And when you mess your incantation you ripped from SO up you get errors with strange terminology. I understand it's benefits and use it, but older simpler versioning systems like SVN had their own benefits.
While there are definitely UX problems, the following aren't:
- needing to memorize branch names for easy use: there is tab completion for this;
- differences between staged and pushed commits: I'm not sure what you refer here. the closest concept to the "stage" term is "staged changes". this is by design; the idea is that while one applies changes within a single commit, some are definitive, some are evolving; for this workflow, it's very convenient. if one doesn't want, they can just add all and commit (preferrably, with a single alias ;-)).
The "most extensive" Git UX problem is possibly the excess of functionality given to the `checkout` command, which in fact is being split.
I think that's just your own personal bias on the matter – 99%, really? To me (IMO), Git is something that's inherently visual and not really suited to a CLI, it has nothing to do with not understanding how Git works under the hood.
It absolutely is. The whole "branch" paradigm evokes topology. The visual structure of the thing you're working with ought to be made clear to you, the user.
If a GUI has trouble when the workflow deviates from normalcy, it's just not powerful enough.
That being said, there will always be users who prefer the command line, and that's totally fine.
Git is well suited to graphical interfaces, since it's quite useful to visualize the commit graph and for viewing diffs. The fact that the git command line tool is quite awful makes this even more of a no-brainer.
I get what you're saying, but not that they necessarily don't understand git. I guess it's just personal preference.
I don't know why, but I've always felt uncomfortable using git in a GUI, but I know a lot of people who love it.
For some reason, anytime I try to use a GUI, I don't feel like I'm really doing git, if that makes any sense. Also, I've personally never felt the need to see the branches visually. And if I do get the itch to 'see' them, nothing a quick
Totally agree with everything on this list, with two exceptions:
* I almost always just do a regular merge to master (rather than squash).
* I use GitKraken which (in the pro version) has a great merge tool.
A lot of people seem to want "clean history" so just I'd just like to put it out there that it's not really necessary (there's nothing wrong with it, of course), and regular merges work just fine. In my experience, the two main reasons I look at git history are:
(1) Checking when a feature was done and what releases it got into
(2) Finding out why a particular line of code exists (blame)
For (1), with software with numbered releases, you end up following the branches anyway. Having the individual commits from feature branches makes you scroll more, but it doesn't make it any harder to follow. If you really don't like seeing them, you can hide anything but merge commits.
Even simpler, if all you care about is whether a given feature branch is done (merged) or not, that's easy to see and the same as a squash-merge workflow.
With continuously-deployed ("cloud") services, things are even simpler - you start at the hash currently deployed and go backwards. It makes no difference if there's one branch or many feature branches (other than scrolling past more granularity).
For (2), the greater granularity from individual commits is generally useful (at least provided that each commit has a ticket number and useful description), and squashing erases this detail.
Also, a nice thing is if you end up merging the same branch to multiple places (master and a release branch, for example) it's obvious what happened by looking at the tree. Contrast to if you cherry pick a squash merge commit, the only way to see that is to read both commits and realize they're the same -- and this is much less obvious if there's other commits between them.
Basically, squash merging doesn't help with either of these, and in fact actively hinders (2).
My team has been doing this for a few years, and I'm not even sure if it was a conscious decision to merge this way (or not do squash merge) but we've had no reason to change. It does depend on every commit being well-formed, but this is pretty easy to ensure in a paid team that works on this every day (whereas with open source I'm not sure it'd work well).
Anyway, like many things in git, there's more than one way to do it, and there's no clear "right" or "wrong" way.
git commit --fixup 6138D3A
git rebase --autosquash --interactive origin/master
to keep a clean history of cohesive commits. Rarely do I change _everything_ required for an objective in one go, I still like to commit as I work, I just like the finished product to _seem_ like I did it in one go, for future maintainers' sake.
And I rebase to catch up with upstream, I can't stand having intermediate merge commits in my history and rebasing lets you resolve conflicts as they're introduced, instead of an all-at-once at the end.
Wow, I always love when a git/shell elder comes along and shows me something like this. I didn't know about `git -c` before, I'm assuming it's setting per-command config values? I've also not used/seen `!_()` before... I'm assuming it's introducing something like an anonymous function and calling it at the end... but why not just break out the body of the function and execute it directly in the alias? Does it help with error propagation?
git will append all remaining command line arguments to the end of the aliased command. Therefore `!f() { ... }; f` is the usual style for this kind of git alias to get access to the arguments as needed with $1, $2, etc. in the shell function.
I know this has been mentioned a million times on here, but for the off chance that someone hasn't heard about it before I'm going to completely disregard that and say that Magit for Emacs is an incredibly helpful tool for interfacing with git repositories that I couldn't imagine working without it. It's by far the best interface to a version control system that I've ever used. It has helped me understand how git works behind the scenes, and allows me to effortlessly work with branching, stashes, merging, rebasing, remotes and much more, without remembering a bunch of arcane git commands.
I find myself more productive on the command line. It just feels "safer", esp. for anything that changes state. And I always prefer not using the trackpad if I can avoid too.
I know without hesitation all commands I use daily. And then I keep a note with the few commands I use less often. An exception is when I want to display changes between version, or conflict resolution.
That being said, I'd like to rely just on my IDE (VSCode) for better consistency, and I tried a few times, but I always return to the command line which is a great tool. For the same reason, I prefer to `CMD-tab` to terminal and `make` rather than build from the IDE.
One of the things that make magic so great is precisely that one does not have to remember all the shortcuts. The author has written and released his own tool for popups and tooltips[0]. The keys are also among the more sane binds you find in Emacsland, so you can often browse by guestimation.
In magit in those Emacs distributions, the question mark brings up a list of available commands and if you type the first letter of a command with multiple letters, it'll show you the names of the actions that start with that key.
use spacemacs and it's much more accessible. magit is phenomenal software and it's both a blessing and a curse that it's tied to emacs... it probably wouldn't exist as easily outside of emacs and it's a small pain to use for people that don't use emacs already.
Spacemacs is my favorite way to use Emacs, I leave the Emacs keybindings for it, but you can still use some of the ergonomic shortcuts from it. I have never used Magit however, I just prefer using a GUI: Sourcetree on Windows / Mac is A+, Gitkraken is fine, but idk if I can justify $60 a year, and SublimeMerge doesn't "feel" ready for me just yet, but if it ever does I may consider it. The main way I do things overall is IntelliJ and Co's built-in git utilities. If something gets complex I switch out and into GitKraken or Sourcetree.
I was happy with the git cli (and occasional tig) when I started using magit full time and initially never understood all the buzz. It did not seem that powerful over what I was using.
After 3-6 months of use, for some reason I had to go back to the cli and then I realized the ease and speed I had got accustomed to.
Magit is magical; use it for some time and try going back to your previous setup.
For those among us of the Vim-family persuasion, there's tpope's ever-popular vim-fugitive[0], with such handy commands as `Gcommit`, `Gadd`, and one of my personal favourites, colour-coded `Gblame`.
Without Spacemacs having me brought to the Emacs worlds, I would have never discovered magit. Nowadays I am soo tied to it that I even have a `magit` script that can be called from anywhere:
I recently started using Magit, and one of the first things I did was cruising through a gnarly interactive rebase full of fixup commits with it. It was amazing.
One of the things I appreciate about magit is that it gives you sane default behaviour. For instance, the -f option when pushing with magit is —force-with-lease, rather than —force, so that you don’t inadvertently overwrite someone else’s work. When stashing, it helpfully prompts by default you to enter a message for your stash to differentiate it from the 10 other stashes you might have on a branch. Sure, these are things you can get on the command line, but they’re not the defaults, and you have to remember how to get to them.
I really enjoy using Magit, since it makes partial commits a breeze. Unfortunately, it's notoriously slow on Windows, so I ended up migrating to Git's interactive-mode commit utility.
Git log is probably the single git command I use most often.
Stash can be useful, but it’s good to know that you can get by without it very easily, there’s nothing you can do with stash that you can’t do with commits & branches, and stash doesn’t have the same safety mechanisms that commits have. I think the biggest git accidents I’ve seen, where people have gotten confused in the middle of a merge and lost their work, are due to using stash.
Full disclosure, I have a wrapper script to make git log output a lot more readable for me, it columnizes and shortens and colors the output. I’d agree the defaults are not amazing, and even with log’s many arcane options it’s very difficult to make it feel smooth.
I personally find `cherrypick` to be occasionally helpful, as a way to jump out of trying to be too git-clever, and simply starting a new branch and pulling in the useful parts of another branch.
How is the PR author supposed to fix it without complex Git commands? Error prone copy and paste?
I _really_ love working with Git, but in my experience engineers unfortunately do need to tap into the long tail of “advanced” commands fairly often. At least often enough that they will quickly be frustrated by it.
(I’m currently helping move a bunch of our engineers from SVN to Git, and while I believe the better ecosystem will be worth it, it does break my heart every time someone has pointless trouble simply because of Git’s CLI.)
> How is the PR author supposed to fix it without complex Git commands? Error prone copy and paste?
Reclone from master (or the active branch if something else) to another directory, merge in their final changes, manually if needed, properly test the result, and sent a new PR with the cleaned up version?
(caveat: I've not used git in anger, there may be more to it than that, but I can't imagine much more)
I suppose who is responsible for dealing with cleaning up updates before merging them into the main project depends upon who wants it merged most: the PR submitter because they want a fix/change/other in upstream so they don't have to maintain their own fork, or the project maintainer because the update is more generally useful to other users or more specifically useful to the maintainer.
If you submit a badly arranged PR or other patch you are creating extra work for the project maintainer(s). Depending on the project and maintainer(s) this may be acceptable or, equally rightly, it may not.
I've had engineers tap me on the shoulder with a proud look on their face while pointing at some crazy mess of branches they've just spent hours futzing with to finally get "right". It would have taken them a fraction of the time to just fix whatever issue they were having by following the advice above. People really take git histories WAY too seriously.
I can see the point of being pathologically protective of your own source history. But the project you are sending a PR to only really need to see the final effect of the changes.
Of course for your own local history the same technique can work, you just keep the old branch as well as the newly cleaned one. Though that might be unnecessary clutter to many.
I got a little tired of running to Google every time I needed a non-trivial git command, so I set out to identify all (and only) the git commands I require for my day-to-day and capture them in one place. The result is this script. It can be run fully on your local machine, no remote accounts required. (But it still shows how to use remote repos!)
I'd also be interested to hear what git commands others use frequently (that don't involve a GUI). I'm curious to know which workloads might require some of git's more advanced features on a regular basis.
(shameless plug): https://github.com/dandavison/delta is a syntax-highlighting pager for git that adds syntax highlighting and within-line edit detection to all diffs displayed by git.
I agree about `git log -p`. A good trick with that is to use it as a quick way to search for a commit that includes a certain string (since the pager is less, one can search back in time with `/`).
Same trick for `git reflog -p`.
`git stash show -p` is another less-known diff command (look at a stash without applying it).
> I got a little tired of running to Google every time I needed a non-trivial git command
Have you read through the gittutorial, gittutorial-2, and especially the gitcore-tutorial manpages[1]? Reading those guides and actually following the steps and examining the results on disk was extremely helpful for me in understanding how Git actually works. Once you've got that understanding, the Git UI seems less arbitrary. You can start to see how it was put together over the years, which should help you use the tool more intuitively and effectively.
> I'm curious to know which workloads might require some of git's more advanced features on a regular basis.
I work on a very large and very old (mid-90s) project, so history-diving and diffing branches is very useful for me. I use all of git-blame, git-show, and git-diff to examine history to learn how the code came to be as it is. Even git-cat-file comes up occasionally when I just want to look at some old object I happen to have a hash for and don't want to look up properly through the porcelain.
We also maintain a bunch of branches and backport modern commits onto legacy branches. git-rebase can help for this, but more often I find myself doing it manually (well, with a bash script) with git-cherry-pick, as it's easier to work with commits without being in the middle of a rebase operation. Knowing git-log's many options to massage history into a useful form for my work comes in very handy here, too.
Depends on how many features you want it to have, and what level of control over it you want to have, I suppose. It's hardly unique in being a tool one needs training to master. How many programming language books are there? Database theory books? Adobe Photoshop guides? Safety instruction guides for power tools? Best practices for building construction... You have to learn a thing to use a thing. Git is no different.
Your argument seems to be that git, like all change control systems, is powerful and requires explanation. I would counter that there are other change management systems I can explain in 5 minutes, such as Perforce. Git is hard to explain because its model is a mess and few or no people naturally grok the model right away.
While nothing is perfect, I think Git's model is extremely intuitive and easy to work with. Perforce is centralized, isn't it? That's a deal-killer for me. I think we'll just have to agree to disagree, which is perfectly fine :-)
In my professional experience the distributed nature of git isn't used. Individual engineers don't distribute patches or pull branches amongst themselves, there's always still a central repository that is "the repo". Compliance audits of code and build artifacts in the business world pretty much dictates this model.
I think the fact that you can use git locally to work within your perforce workspaces indicates some commonality among the two models, but git has way more features, edge cases, and jargon.
gittutorial I had seen before, but the others I had not. Thanks for that, I'll give them a go.
Wow, mid 90s! Git came out in 2005, so I assume they started out using some previous version control and then switched to git? That must lead to some serious knots. Is there a way to retroactively migrate source control history into git? Depends on the legacy source control tool, I guess.
I think git-blame and git-cat-file are probably the next commands I need to explore in depth. Will investigate those further.
Thanks for sharing your gitconfig as well. Always interesting to see how others are streamlining their workday.
Yes, it was migrated from CVS in 2005[1]. Between then and 1998, it used CVS. Before 1998, It looks like only releases were tracked[2]. I'm not sure what system was used, if any. But yes, it's all migrated into Git, including even the dates.
I just found out today about git worktree[1], and I think it's a very powerful feature more people should know about, but it seems used less than it could.
Basically, you can do a working copy of the git environment without pulling the repo again, so you have only one .git folder.
A godsend if you, like me, work on gigantic repos that build unbearably slowly and want to be able to use multiple instances of the same repo at the same time.
Compared to the previous technique of creating a new separate local clone for unrelated simultaneous work on a project, it's also glorious when it comes time to clean up and delete no-longer-needed extra working directories.
After `make clean` and `git status --ignored` shows nothing worth preserving, you can delete the worktree (via `git worktree remove`) with impunity. No more paranoid checking that there is no valuable work stashed or hiding in other branches before typing `rm -rf`, as those are shared with the main worktree so won't be lost.
Try Gitless[0]. It's an alternative 'UI' to Git which is kind of build top down instead of bottom up. The commands are much higher in the abstraction tree and clear single operations and dropping some concepts like the staging area. It operates on the Git database the same way 'normal' Git does so you can always fall back.
My understanding is that in git-land, "porcelain" and "plumbing" usually refer to a specific dichotomy of high-level vs. low-level git commands, rather than further abstractions over the commands such as a GUI.
Thanks for sharing, but I feel like it's more like 99.999%. The essential commands for development are really a lot less than this. Maybe it's a list for release managers / sys admins?
Committing, pushing, handling local branches, updating, merging, rebasing, cherry-picking, squashing and stashing is almost all that everyone uses, and several of those not even on a daily basis.
After years of wanderings, I've settled with Fork which is a a delightful GUI.
Polished, feature-rich, and still improving on a regular basis. Kudos to the authors!
Same, I spend about 50% of my time in git on the command line, the other 50% in Fork. I've used SourceTree -> Tower -> Fork and I'm very happy with where I'm at now.
One tip I give people who feel overwhelmed by Git is that unless you are an edge case, you can probably do most of what you need to get done with these six commands:
branch
checkout
add
commit
pull
push
Reading and understanding the documentation for those six core commands isn't a big investment, and it will pay off if you're doing software or documentation development.
I'm personally a huge fan of https://www.gitkraken.com/ (I also used to work there.) Don't have to memorize git cli commands and get beautiful visuals on top of that.
It’s been a long span of time since I’ve used GitKraken or Atlassian Sourcetree, but GK had some serious performance issues with larger repos in my experience. Very clean app, though, and I did enjoy it.
Right now, Im partial to Sublime Merge[0] as it’s fast, has a clean UI for diffing, shares many similarities with Sublime, and also shows the use what git commands they’re running by hovering. It can be a very effective tool for learning git. It has the same evaluation structure as Sublime, so that’s a bonus as well.
Yeah there definitely used to be some perf issues. There have been some massive improvements recently though for big repos. You should give it a try again!
I do appreciate that this is probably a cracking tool for beginners and juniors. If it was skilling someone up to git using this tool or not at all, damn right I’m choosing this.
But I can’t bring myself to run a web browser to run git commands either. The git binary on my system comes out at 2.5Mb and the MacOS .app bundle for this is 346.5Mb.
Yeah pretty much every app I run nowadays is Electron. Don't love that, but they bring me enough value that it's 100% worth it for me. I'm with the other commenters that I don't like having to google a git command every time I have to do something more complex than add/commit. GK is perfect in that sense
ugh i hate GUIs, for me those are a middle man. With cli, i can "feel" git. with GUI i'm just telling a 3rd party to talk with git for me. That 3rd party can also screw up some communication, just like in telephone game.
They also fail to at least show you the conversation before it starts, so you could align your ideas on what can go wrong. I had to ditch tortoise<vcsname> for this exact reason in our entire division, except for simple initial checkouts. That damn thing always tried to append some “--qwerty --asdf” to every innocent command it could be asked to perform.
This kind of thing is why I'm very glad that the Team Explorer git plugin in Visual Studio has become decent enough that I don't have to drop into the cmd line, pretty much ever.
I'd say I literally use pickaxe (-S) and the -G one a few times a week. Often to answer questions I hear around the office. If you are not familiar with it, I think its an amazing. Err... I guess...check it:
If you want git on the command-line, but don’t want to remember commands, try lazygit[0], it’s changed what I’m comfortable doing with git because it can tell me at any given moment what I can do.
Set merge.conflictstyle=diff3!!!! The default merge style is utterly useless, I have no idea why it is the default. diff3 gives you three pieces of information: the status before at the common ancestor; the status on the current branch; and the status on the to-be-merged branch. You can then understand what changes were made on each branch, and decide how to merge them intelligently. Without that common ancestor, it's way way harder to understand what you're looking at.
I’m a fan of using Perforce merge (p4merge) with git. It’s free to download and use, and easy to integrate with git. I remember thinking the UI was weird the first time I used it, but since then I’ve tried a bunch of merge tools and haven’t found something I like more. It shows common ancestor by default, and lets you hand-edit the merge section, which not all merge tools do.
I have used Kaleidoscope [0] for a few years now to resolve merge conflicts, it's a powerful and beautiful app, and the key bindings are great to fly through conflicts and select "mine", "theirs", "mine-then-theirs" or "theirs-then-mine". And you can also hand edit the resolved version. It's also great for git diffs and file/dir diffing, and can be summoned from the command line with `ksdiff`.
The ones listed as options for git mergetool are all good. Personally I use kdiff3, it has a simple interface for choosing conflict resolutions and can often resolve some of them on its own.
many modern IDEs will help with this, ie. visual studio code. It will highlight and name the conflicting code by branch.
When the conflict occurs you can do git status and the highlighted files are those with a conflict -- at that point you can just navigate there in the code editor of your choice and look for the <<<< blocks. After you get those files into the state you would like then you run 'git commit' to resolve.
Developers having hard time to master GIT after so many years is just a good argument to "Why we need GO more than Scala" and a very disappointing reality at the same time.
- Always pull w/ rebase for the current branch.
- Always merge other branches into your current branch, eg master -> feature.
- Always stage individual chunks of code one at a time to make sure I'm committing the right stuff.
- Always squash feature branches into a single commit when merging back.
- Stash changes if needed when switching branches.
- Cherry-pick one-off commits if needed.
- Append a previous commit that I haven't pushed yet if I happened to forget something.
- For complicated merge conflicts I switch to Visual Studio Code which also has a great GUI.
I think this covers 99% of the stuff I encounter in my day-to-day.