22 ms·
Git techniques
- kazinator 5y ago> As of git 2.11 one can select a specific stash to be popped instead of just the latest stash using git stash apply n where n is the stash number. You can pop or just apply a specific stash using git stash { pop | apply } stash@{N} This has worked as far as I remember; I have a 1.6 installation somewhere where I can confirm it, if necessary.
- mrinterweb 5y agoI feel that having a default merge strategy to squash and merge all commits in a branch is a version control anti-pattern. This discourages thoughtful and frequent commits that express the intent of a change because all the commits are just smashed together anyway so why bother. I think context and intent is lost when looking through git history of large smashed commits. I prefer using a precommit hook to automatically prepend a Jira ticket number to each commit so when you look at the history you'll see multiple commits grouped together with the same ticket prefix, but the commits still retain the intention of the commit. Knowing that commits will not be squashed promotes devs to make meaningful commits. I still advocate for cleaning up and squashing your own commits as you see fit with an interactive rebase before your branch is merged. Having discrete commits can also help when running git bisect to find when a bug was introduced so you identify the specific commit instead of a feature being merged.
- halestock 5y agoI've seen this argument come up a few times, and the best suggestion I've heard which could make both camps happy is to add the notion of commit groups. You could view a pr in history as a single commit group, or see each individual commit for the full context.
- hnlmorg 5y agoIs commit groups a feature or a wish list? I hadn't heard of it before but a Duck Duck Go search only throws up a blog post discussing the desirability for such a feature in git.
- periodontal 5y agoYou can get something somewhat similar with "always create merge commit" workflows (no fast forward) and changing your tooling to look at the first-parent-history by default. This view will have one commit per merge, but you can choose to follow the second+ parent history for a given commit to see what went into it.
- globular-toast 5y agoOr just include a group name in the commit message. No need for empty merge requests in your history. Since most people use issue tracking systems, just prepend the issue number to each commit message in the group.
- maximilianroos 5y agoIME this depends on whether people make large or small PRs in a repo. If people make small PRs, committing to mainline as they go, then squashing each PR fits well.
- lysp 5y agoThat's why I prefer feature branches with merge commits. Your dev branch is clean because each merge-commit is a single commit per task. So you can see which tasks were merged and in which order and what file as a whole were changed in each task. If for debugging / code review or any other reason need to look at specifics, you can look through the feature branch commit by commit to see what was changed and why. It's best of both worlds. Similarly merging dev into master/main. You get a release by release view of what files were changed in a single merge commit.
- rvdginste 5y agoCompletely agree with parent and grand-parent. For me, a nice clean commit history is an investment in future maintenance. Following the rules described above means that you get a lot of context on why code was changed: * the invidivual commit which should contain a description of the change if the change is not self explanatory or not 'intuitive', the individual commit should consist of only 1 functional change * the commits surrounding it, the feature branch (clearly visible because of the merge commits) * the issue number on the commit itself and possibly in the merge commit I've found all this context very informative for projects that are in maintenance mode and still need changes from time to time. Obviously, the higher the quality of the commit history, the higher the quality of the information you get out of it. Meaning: if you put a rename with an impact over the whole code base (because the original name just happened to bother you that day) together with a bugfix in the same commit and the commit message has the very informative text 'fix', and the referenced issue mentions 'add support for blah' (but the commit obviously does not implement anything related to 'blah'), then... well, yeah, then how you organise your commit history does not really matter.
- lysp 5y ago* the issue number on the commit itself and possibly in the merge commit Absolutely this too! My process is to create a feature branch named "ISSUE-123-issue-description" The benefit of this is all changes are tracked (and tested) against a specific issue in bug management software. It also prevents people making small / unrelated changes or fixes in association with another task. If these are grouped in together in a single and unrelated task they won't be trackable or testable.
- nuerow 5y ago> This discourages thoughtful and frequent commits that express the intent of a change because all the commits are just smashed together anyway so why bother. This is only the case if said squashing just bundles commits without context or consistent logic. If merges to a mainline branch consist of feature branches whose pull request was already approved after a couple of iterations then the end result is a cleaner commit with it's history thoroughly audited. In practice it's equivalent to a fast-forward merge of a single-commit feature branch that just happened to be nearly lined up with mainline.
- astrobe_ 5y agoAgreed. This is when you believe that your program should at the very least compile (or pass tests) at any point in the history. In this case a commit must be a consistent and related set of changes. In other words, a commit to us is sort of like an "atomic" change, something that cannot be split or else more or less bad things happen. I have trouble conceiving a better way to use Git when you really care about the readability of your history. in some cases I don't care about readability though. On hobby projects I sometimes use Git more like a file transfer and synchronization tool. In this case I don't give a huck about how the history looks like. Just like with code, the more readable this history is (in terms of what features/fixes are in there at some point in time), the better.
- rectang 5y ago> This is when you believe that your program should at the very least compile (or pass tests) at any point in the history. I only expect that at merge commits, which I can see with `git log --merges`.
- bonzini 5y agoWhy would you? Linux (and any other C or Rust open source project I have worked on) compile and work at any commit.
- 5y ago
- globular-toast 5y agoSomething people don't get about commits is they have multiple purposes. A lot of this is due to still entrenched assumptions and practices from older, inferior version control systems. There are at least two types of commit in git: a savepoint and a version. A savepoint is what happens during development on a branch. Git makes it super easy to make many, many savepoints throughout the day. These help you as a developer because it gives you something to fall back on if you make a mistake. But most of them should never be exposed to anyone not directly working on the branch. A version is what you share with others. A version is a fully working version of the software that can be reasonably checked out and put through a release process at any time. Usually a version will be unit tested but not subject to the same rigorous tests as a release. There is a direct analogy here with database transactions. Just replace version with transaction. Often while working you will find it's possible to write the version commit right away. This is usually for more trivial fixes or in some cases when a commit is required for something like a database migration (when things need to be deployed in stages). Other times you will need to make several savepoints before you get to a new version. This is what rebase is for. Many of those savepoints don't belong on the master branch as they are often fixing stuff you haven't even committed to master yet. Git has a few tools to help you defer rebasing until later. In particular you can make fixup and squash commits. These will be normal savepoint commits, but they will be labelled in a way that later you can issue an "autosquash" command to automatically rebase these into version commits.
- WorldMaker 5y agoThere's also nothing wrong with leaving "savepoints" type commits in a branch. Sometimes "I stopped here and took a break" is still useful information to have later on. Git provides a DAG and you can use a --no-ff merge to build your "version commit" from the sub history of its "savepoints". You can follow one parent of the merge to the next "version commit" or you can follow the other parent through the intermediate "savepoints" that built it step by step. You can use --first-parent today for most git operations to get "clean views" no matter how complex the DAG web is beyond it. I think a lot of these debates would "go away" if more people and user interfaces defaulted to --first-parent and "drill down" navigation rather than firehose of the complete graph and confusing (but pretty) "subway diagrams".
- daitangio 5y agoI agree. I do not use squash, I prefer to have a feature branch and live it alone after a merge. Anyway some workmate use Squash when accepting pull request on GitLab/GitHub as a general workflow suggested by such tools and in context where trunk based development is not feasible.
- jonkoops 5y agoI have to disagree with this as it relies on the assumption that every commit on a branch is logical and descriptive. In my experience a lot of PRs will have small commits that have poor names as they go through a review process. If you merge this using a regular merge commit or by rebasing the commits on the target branch this creates a lot of noise for those who look at the commit history. In my opinion it is best to squash all commits into one before rebasing it on top of the target branch. During this process any information that is considered important for the history can be preserved by leaving it in the commit body.
- rectang 5y agoAs someone who carefully crafts my git history, I hate it when somebody smashes my work.
- omegalulw 5y ago> I have to disagree with this as it relies on the assumption that every commit on a branch is logical and descriptive. In my experience a lot of PRs will have small commits that have poor names as they go through a review process. There's your problem. Code reviews should not allow such commits to pass through.
- t3h2mas 5y ago> Code reviews should not allow such commits to pass through. Are you suggesting that your code review process has a stage for combing through commit messages? What does this look like? If the third commit message of 15 isn't up to par what happens?
- derekperkins 5y agoYou ask them to fix the commit message. Every git GUI should support that by now, so it should be a 1 minute fix, even for junior devs.
- rzwitserloot 5y ago> I have to disagree with this as it relies No, you didn't read the comment fully, or you only disagree with part of it. Because, you clearly missed this part: > I still advocate for cleaning up and squashing your own commits as you see fit with an interactive rebase before your branch is merged. If you do that, you don't end up with 'small, poorly named commits'. Or if you do, you have a lazy programmer / an idiot programmer in the team. Which certainly happens, but, they ruin everything. You can't start shooting down processes, languages, tools, or anything else in the programmer space __just__ because some moron who abuses it ends up in a bad place. You need to show that a tool / feature / process / hook / etc turns otherwise fine, capable programmers into idiots in order to advocate for its abolishment. Not the other way around, or you end up with a blunt rock and a club and are then debating that they're holding the club at the wrong end.
- dahart 5y ago> I still advocate for cleaning up and squashing your own commit Completely agree. And I suspect the increasing frequency of squash-merging is mainly to avoid having to do the work of cleaning up and commenting individual commits in a longer sequence. I can see this both ways, it really is faster and easier to squash. And you’re right, it really does bury some context and functionally makes large changes harder to read or bisect or revert or modify. One benefit to squash merging that you might have overlooked is that it can encourage frequent (and messy) committing, knowing that the churn will disappear without having to work hard to clean it up. This does, in a way, make the git workflow more appealing and easier to manage for more people.
- mrinterweb 5y ago> One benefit to squash merging that you might have overlooked is that it can encourage frequent (and messy) committing, knowing that the churn will disappear without having to work hard to clean it up. I've noticed the opposite. Developers who know all of their work will be smashed into one commit at the end tend to not commit as frequently, and the commits they do make are just checking in all of their work at intervals. It is more of a process of saving state. It doesn't matter how frequently they commit if all the commits will become one.
- beermonster 5y ago> Using git can be daunting at first. Like my good friend Chris once said, "everyone knows the happy path but the minute it gets hairy we're all screwed". Sounds familiar. Lots of people just learn a few template use-cases, but don’t take the time to learn/study the tool. You might argue you shouldn’t need to but Git isn’t intuitive and does require investment. It comes with its own terminology; there’s no point just guessing what a branch is for e.g.. Like all powerful *nix cli tools, if you don’t you’ll shoot yourself in the foot one day. Even if you want to use a GUI interface, it’s no substitute for learning how git works behind the scenes. Sometimes when I offer to help a colleague that’s got themselves into knots the sad thing is often they can’t even explain the problem they got into.
- KronisLV 5y ago> Like all powerful *nix cli tools, if you don’t you’ll shoot yourself in the foot one day. Surely we can do better than this? A tool being powerful should be no excuse for it having footguns! For example, see "Why SQLite Does Not Use Git": https://sqlite.org/whynotgit.html https://sqlite.org/whynotgit.html In my eyes it addresses many of the problematic bits of Git and offers alternatives to them. Admittedly, it was sad to see other VCSes like SVN die out, since there were some things that they did more clearly than Git (e.g. revision numbers), despite their other shortcomings. Currently, for many Git is essentially forced upon them and they have to learn commands that they don't understand to use it, much like you said. If version control were easier and more intuitive, then more people would adopt it and the ones using it would be more efficient with it, rather than people messing up their repositories and others blaming them for it - even though they'd be right, that sort of elitism also doesn't help much, since clearly some mistakes are easier to make than others. I think that most of the systems out there will eventually be improved or rewritten until finally they are both powerful and usable, even Git probably isn't immune to this. Whether the incentives to do that are there now (since GitHub, GitLab and others are already de facto within the industry) will only affect whether this is done in the next 50 or 100 years. Here's an issue that we ran into this week: - at work, we have a "main" branch and a "development" branch - a new "feature" must be based off of "main", but eventually moved into "development", since that's what the test environments are configured against - thus, we have a "feature-development" branch, into which we merge "feature", to not sacrifice our ability to put "feature" back into "main" without the "development" changes, if ever needed - then, we merge the "development" branch into the "feature-development" branch to solve any conflicts or do any refactoring that we need to ensure that those two branches play together nicely, before merging "feature-development" into "development" - this week, someone did the opposite, they merged "feature-development" back into "feature", thus moving all of the "development" changes into "feature", which we can't allow - the solution? who knows, one option would be to force push the earlier position of a branch, thus erasing the merge commit, but force pushes are considered a bad practice - what we opted for in the end was having another commit that reverts the merge (through the GitLab UI), however now the history still shows that "feature" has 100+ commits in it - the problem that we might run into down the road now is that when we merge "feature" into "feature-development", we'll probably also carry over the revert commit, which we'll then need to revert or do something so that the "feature-development" branch doesn't have all of the "development" changes essentially removed (which may or may not be true) All of that pain, essentially caused by one bad merge, whereas other colleagues are now also asking me about rebasing. I don't feel like i know Git well enough to be the "go to guy" and would rather just write the code i need for the features, rather than worry about the branching strategies and what colleagues have done and weird client requirements for what branches must be used as a base since there are not enough test environments. Furthermore, GitLab not having an easy way to say "i want this commit gone" is equally annoying. If they offer you to do reverts, surely they can also ensure the equivalent of a force push? Then again, chances are that it's too early for me to say anything vaguely accurate and even so someone out there probably can solve all of it in a line or two. Nonetheless, it feels to me that an easy to use tool would expose solutions for the most common problems in an approachable way.
- revskill 5y agoI never touch the git rebase at once in my 10 years of programming. It's born to solve a problem little people face ? I'm not sure though.
- blowski 5y agoOn large monolithic repos with a lot of people committing features independently, rebase helps reduce the noise in the history. If you mostly work on small repos (e.g. microservices) or repos with few contributors, then it makes sense you haven't needed rebase.
- 3np 5y agoHow do you make your commit history clean and reviewable when submitting a PR? How do you keep your local branch up-to-date with upstream without a mess of merge commits? I use it continuously throughout any workday, if I'm doing contributions that day. It's also a good idea to set your pull strategy to rebase, as recommended here: https://sdqweb.ipd.kit.edu/wiki/Git_pull_--rebase_vs._--merge https://sdqweb.ipd.kit.edu/wiki/Git_pull_--rebase_vs._--merg... git config --global pull.rebase true
- KronisLV 5y ago
- rom1v 5y ago> We therefore squash and merge our feature branches onto dev when a PR is opened and merged. Regardless of how many commits a feature branch has, once it is merged, all the commits are squashed into a single commit So several steps to implement a feature are mixed up into a single change :/ That looks awful to me, when you need to read the history to understand how and why.
- reilly3000 5y agoI’m an imperfect human, but I don’t always see the value in having 5 commits that say “fixes lint error” and another few “fixes typo” in the git log for every feature branch. Perhaps there is a middle way.
- rom1v 5y agoA PR may contain several distinct meaningful related commits, this has nothing to do with typo fixes. But even then, when you want to revert or cherry-pick a commit on another branch, you don't want random unrelated changes (which increase the risk of conflicts). Also, a single commit containing 10 squashed commits doesn't help for git bisect.
- ivalm 5y agoWould you be ok with history rewriting in feature branch prior to merge? Seems like potentially best of both worlds.
- rectang 5y agoI do minor history rewrites on my feature branches all the time. 1. Fix typo or other small nit. 2. `git add -p` to add only the small change. 3. `git commit -m fixer` 4. `git stash` 5. `git log --oneline main..` and copy the SHA of the commit I want to fix. 6. `git rebase -i SHA~` 7. In the text editor launched by `rebase`, move the "fixer" commit after the one I want to modify. 8. For the `fixer` commit, change "pick" to "fixup". Save and quit, allowing the rebase to complete. 9. `git stash pop` This works most of the time, so long as the nit I want to fix isn't too far back in history. For those nits that aren't easy to fix with the workflow above, I just create an ordinary commit and leave it. It's annoying to have 5 "fixes lint error" commits in every feature branch, but it's fine to have them every once in a while.
- globular-toast 5y agoI would strongly encourage getting rid of the dev branch. It is redundant in almost all cases. Instead think of the master branch as the integration branch. This is where everything gets merged ready for a new release. But the master branch itself is not a release. You can automatically deploy the master branch to a staging or "next" environment if you wish. For releases, use tags. That's what they are for. If necessary you can make one or more "maint" branches where you backport important fixes from the master branch on to release branches to create patch level release versions.
- cjpearson 5y agoJust to add on to this, I've found using dev/master or any other two-trunk setup also tends to encourage bad habits. One branch is seen as the 'production-ready' trunk while the other becomes 'broken-or-incomplete-code-ok' trunk. When it comes to releases maintenance branches like you suggest are a lot more flexible and allow you to support old versions. Releasing v1.1 after v2 doesn't really work if all your releases have to be a merge to master.
- bazhova 5y agoUsing dev and master branches is anti pattern and does not scale. Soon your QA team wants a QA branch. Then business wants a branch for demos. The DevOps ... Etc. Use tags. You only need one branch called "main". The way open source projects do it is best, look no further!
- mdaniel 5y agoI would guess that pattern comes from the git-flow diagram: https://datasift.github.io/gitflow/IntroducingGitFlow.html#how-it-works https://datasift.github.io/gitflow/IntroducingGitFlow.html#h...
- WorldMaker 5y agoAgreed. It hurts environment reproducibility. It's best when the same binaries flow from environment to environment. That way you know that the binary you built and was tested by QA in the QA environment (not branch) is the exact same build moving to Production, not some entirely fresh rebuild possibly affected by unintentional merge conflicts or hidden integration problems.
- curious22 5y agocould someone tell me which is the git UI client shown in the screenshots of the blog post? https://riskledger-website-media-uploads.s3-eu-west-1.amazonaws.com/git-article-part-I-5.png https://riskledger-website-media-uploads.s3-eu-west-1.amazon...
- xorcist 5y agoThose commit messages leaves a lot to be desired. "Changes print messages again" How helpful is that? In what way were they changed? What was the intention behind the change? Is there a ticket or a feature request behind it? Why was that particular change chosen instead of all the other ways to achieve the same effect? It's also considered prudent to keep to a common format. These are even written in differing tense, where imperative is by far the most commonly preferred.
- hnrodey 5y agoMan, I really feel for people who struggle with Git. I should know - I was one of them many years ago. Many day-to-day problems with Git all have something in common which is that the user has got their repo in to some state they do not understand. And most times (as comments have mentioned) they don't even know what they did to get themselves in their current pickle. The quickest path to resolution is usually a hard reset to the server version of the branch then restore the commit(s) that user had made. If you have pending changes then handle those first. It doesn't matter much what they are just commit or stash. If committing don't even think about the message just use `wip commit`. Finally, note every commit id that has your work you want to keep around. Might be 1, 5 or 20 commits. Okay, now hard reset. `git reset --hard <remote_name>/<branch_name>` Great, now you're at an acceptable state. Finally, restore your previous commits use cherry-pick or `git stash apply` to get your stuff modified. If cherry pick then it's `git cherry-pick <sha_oldest> <sha_next_oldest> ....` On to the next problem...
- zibzab 5y agoHere is a crazy idea: 1. Commit your your local changes, squash if you want. 2. Do a pull --rebase 3. Push If this doesn't work, it's your own fault. Start over.
- yboris 5y agoMy favorite git-related thing is `diff2html` so I set up an alias `diff` which will open the browser and show me all the changes I've made to the branch: https://diff2html.xyz/ https://diff2html.xyz/
- cebert 5y agoI’m surprised so many organizations use the term “master” for the primary branch instead of the more inclusive “main”.
- patrick451 5y agoI'm glad so many haven't wasted time pandering to this woke nonsense.
- cryptonector 5y agoSorry, but no, a branching workflow won't scale if you have thousands of developers pushing to the same repo. Those pretty `git log --all --decorate --oneline --graph` images won't work when you've got thousands of branches, and the history on the mainline will be poor and of low utility. What I was taught almost 20 years ago at Sun Microsystems (RIP) is a rebase workflow. That was back before Git. We were using Teamware of all things, and we were using it with a clean, linear upstream history workflow. Our particular rules were: - linear history upstream - absolutely no merge commits (at Sun they were called "merge turds") - one commit per-bugfix, though one commit could fix more than one bug, with a separate commit for test changes - one commit per-project - but otherwise one push could push many commits We also had rules about commit titling, naturally. Sun had been using that workflow since 1992 as I recall, so they used that workflow for 16 years, with several thousand developers pushing to OS/Net (core of Solaris). We even had rebase --onto. The workflow for projects went like this: - devs push to a project clone of the upstream - gatekeeper takes care of build issues and preps to push to upstream when project is ready - every so often the project repo ("gate") would rebase onto the upstream head, then devs would rebase their clones onto the new project head - eventually the project would rebase onto and push to the upstream - project repos ("gates") got archived when the projects completed That workflow scales very very well. The resulting upstream's history is very clean, with just: commits for bug fixes, commits for tests for bug fixes, commits for projects, commits for release-making, and the occasional follow-up to a commit that fixed minor issues with that commit (e.g., `12345 Crash in blah blah (fix style)`). I strongly recommend it.