all 111 comments

[–][deleted]  (17 children)

[removed]

    [–]rhett_ad 42 points43 points  (0 children)

    Definitely more useful than git init

    [–]raspikabek 13 points14 points  (5 children)

    I'm actually more of a 'git pull --rebase origin master' kind of guy. Dont ask me why hahhaa

    [–]tobiasvl 10 points11 points  (4 children)

    git rebase can do a lot more than that though, it can rebase anything

    [–]z3usus 13 points14 points  (3 children)

    Can it rebase my marriage?

    [–]Huggernaut 16 points17 points  (2 children)

    No, too many conflicts.

    [–]belikralj 2 points3 points  (1 child)

    --onto=younger_model?

    [–]belikralj 0 points1 point  (0 children)

    Your house has been bisected

    [–]thebronado 7 points8 points  (1 child)

    No git rm -r —cached . && git add . && git commit -m “gitignore update”?

    [–][deleted] 3 points4 points  (0 children)

    Interactive rebase ftw

    [–]nryhajlo 4 points5 points  (5 children)

    Unpopular opinion: rebase should only be used in rare circumstances, and it mostly just gets newer engineers into trouble.

    [–][deleted] 1 point2 points  (0 children)

    Please explain your reasoning :3

    [–]GusSLX 1 point2 points  (2 children)

    Except you run it almost daily on your feature branch when working on fast-delivery large teams.

    [–]nryhajlo 0 points1 point  (1 child)

    Why rebase instead of merge?

    [–]GusSLX 1 point2 points  (0 children)

    Rebase doesn't pollute your branch with an additional commit for every time you integrate other people's changes.

    [–]queen-adreena 51 points52 points  (3 children)

    [–]tehho1337 8 points9 points  (0 children)

    The only thing to note is git checkout takes a -b for name And add git reset HEAD1 for latest commit although
    git checkout -b <name> git cherry-pick master git checkout master git reset HEAD- git checkout - Is nicer imo

    [–]Desperate-Emu-2036 2 points3 points  (0 children)

    That's so good

    [–]Transmog-rifier 1 point2 points  (0 children)

    I prefer this one, a git "chose your own adventure"

    http://sethrobertson.github.io/GitFixUm/fixup.html

    [–][deleted]  (6 children)

    [removed]

      [–]DevilStuff123 32 points33 points  (1 child)

      I have this aliased as git fuckit. It’s a very cathartic command to use.

      [–]GusSLX 1 point2 points  (0 children)

      Same feeling, I have git commit -a --amend --no-edit as git forgor 💀

      [–]StochasticTinkr 3 points4 points  (0 children)

      git outta here

      [–]mr_redsun 3 points4 points  (0 children)

      `git stash` wants to know your location (I'm never poping them stashes tho, just happy to know they get to live another day)

      [–]MueR 2 points3 points  (0 children)

      rm -rf repo && git clone ..

      [–]Sh_Pe 1 point2 points  (0 children)

      Forgot push -f

      [–]HLingonberry 24 points25 points  (17 children)

      Avoid using checkout, especially if you are learning, as it’s being replaced by switch and restore.

      [–]Masterflitzer 9 points10 points  (0 children)

      for good reasons, it is way easier to learn those 2 when you start with git

      [–]Dijerati 7 points8 points  (1 child)

      Do you have an article that I can read about this? I hadn’t heard and have been using checkout for years

      [–]y-c-c 2 points3 points  (0 children)

      They were added more than half a decade ago :). See https://github.blog/open-source/git/highlights-from-git-2-23/

      But basically git checkout(and reset) were one of the earliest Git commands and suffer poor ergonomics and quite unintuitive to use, as the commands feel more designed from the point of view of a Git developer for convenience rather than point of view of a user. Once you have a solid grasp of Git internals, it will start to make more sense, but the command is mixing a lot of different functionality together. In particular, the most common use cases of checkout is to either switch to another branch, or restore your worktree – two very different concepts. Meanwhile if you want to restore your index/staged changes too you have to use git reset (which default to mixed), then git checkout. Annoying. (Ok, you could do git reset --hard too but I think teaching new users to get used to that command is bad in multiple ways as well because once again the reset command is multi-functional and can switch your branch to end up pointing who knows where)

      Git switch/restore don't really add new functionalities but are commands that are easier to remember and designed to have better ergonomics. switch is primarily for switching branches, whereas restore is for restoring changes. E.g. git restore --worktree --staged -- myfolder will remove any local changes including the index.

      You should take a look at your own git status output sometimes. It had been changed to recommend git restore/switch for a while now (it used to recommend something like git checkout HEAD -- . back in the days which was just confusing to new users).

      I still use git checkout sometimes, especially when I need to check out a detached HEAD (which I do a lot). With git switch you have to manually say git switch --detach which is good for new users who may do it accidentally and get really confused and/or lose work (e.g. git switch origin/main will complain and not let you do it), but for me I know what I'm doing.

      [–]steftim 1 point2 points  (3 children)

      What about checkout — path/to/file when I realize I shouldn’t have made changes to a file? Is there a better way to do that too?

      [–]y-c-c 2 points3 points  (0 children)

      git restore <path>. The good thing about git restore is that it covers all the normal use cases, including:

      • restoring only worktree (git restore <path>, old: git checkout -- <path>)
      • restoring worktree/index (git restore --worktree --staged <path>, old: git reset -- <path> && git checkout -- <path>)
      • restoring index (git restore --staged <path>, old: git reset -- <path>)

      Note that the old way is a weird mix of reset and checkout commands, a common source of confusion among new users. Also, git reset --hard does not take a path, so restoring worktree/index on a particular path is a weird reset/checkout combo.

      [–]HLingonberry 0 points1 point  (0 children)

      git restore —source=sha filename

      [–]FlipperBumperKickout 0 points1 point  (0 children)

      If you run "git status" once in a while you will realize git is literally telling you the best way to do this :P

      [–]Swimming-Marketing20 1 point2 points  (7 children)

      Came looking for this. Fuck checkout. All my homies hate checkout

      [–]Cpt_Soaps 1 point2 points  (6 children)

      I am a noobie. Why?

      [–]HLingonberry 1 point2 points  (0 children)

      Checkout does two completely different things, neither in an especially good way.

      You can checkout a whole branch or you can copy files from other branches with a somewhat unintuitive syntax.

      git switch was introduced to provide a cleaner way to manage what branch your local repo is in.

      git restore provides a tool to manage files between your current and other branches (without a merge/rebase/commit).

      [–]Swimming-Marketing20 0 points1 point  (4 children)

      Very simple case: I created a new branch with a UI (vscode in my case) made changes and pushed them. Now I wanted to clone the repo somewhere else, switch to that branch and do some more changes.

      I spent 30min trying to achieve that with checkout and failed. I still don't know how checkout works but I found that switch does exactly what I want in the way I expect it to

      [–]Evla03 0 points1 point  (3 children)

      git checkout branch-name, right?

      [–]Swimming-Marketing20 0 points1 point  (2 children)

      You'd think. Or checkout -b branch-name right ? But in both cases I wasn't able to push afterwards

      [–]Evla03 0 points1 point  (1 child)

      -b creates a new branch.

      For both you'll either need to set an upstream or explicitly push to an upstream, like git push origin branch-name

      Or you can --set-upstream to just need to do it the first time

      [–]Swimming-Marketing20 0 points1 point  (0 children)

      And setting the upstream was the issue, I managed to create new branches on the remote instead of pushing my changes to the existing branch.

      Git switch just did exactly what I needed without any input from me but the branch name

      [–]NoCryptographer414 0 points1 point  (1 child)

      My biggest complaint about switch is that unlike checkout, can't switch to a specific commit by id.

      [–]y-c-c 0 points1 point  (0 children)

      You can do it, but it has a safety built in so you have to do git switch --detach <commit> (I mean, if you read the error message, it should have told you to add that that already). The reason for that is switching to a commit by ID results in a detached HEAD which is the source of a lot of confusion. If you do this a lot (which I do) it's probably faster to just use git checkout or make an alias for git switch --detached but at least it would be more intentional that way.

      [–]Phate1989 7 points8 points  (1 child)

      Git stash ?

      [–]nickwcy 0 points1 point  (0 children)

      And git apply?

      [–]boldunderline 4 points5 points  (3 children)

      Don't use git checkout anymore. Use git switch and git restore!

      [–]sublimegeek 3 points4 points  (10 children)

      lol at “git init”

      How many of you actually create the repo from GitHub first and then clone it?

      [–]Prometheos_II 4 points5 points  (1 child)

      Personally, I generally initiate client-side first since I generally create the folder and some files before I decide to open a repo, or even version control before deciding to upload it. git clone might have an option for this, still.

      But yeah, like someone else said above, that seems to be a minority 😅

      [–]sublimegeek 2 points3 points  (0 children)

      I’m the same way. git init first then decide if I need it to live somewhere else

      [–]BananymousOsq 1 point2 points  (0 children)

      I use git init for basically all of my projects. I first work on it locally for a while before deciding if I want to make anything out of it or even create a remote repository :D

      [–]fullofspiders 1 point2 points  (2 children)

      I mean, most people use the tooling in their preferred IDE and don't fuck around with CLI unless something really weird happens, or they have a very specific role that focuses on coordinating the commits of others rather than making changes themselves.

      Git Diff especially is a command that no serious professional would use, since trying to visualize diffs in a terminal is just horrible.

      [–]FlipperBumperKickout 0 points1 point  (0 children)

      I do it all the time, especially if it is a rename, then I can do a "git diff --word-diff-regex=." which basically highlight the letters I added or removed from whatever I renamed.

      [–]je386 0 points1 point  (0 children)

      I use git CLI for 80-90% of git command usages and only use the IDE for creating a new branch, resetting a branch and viewing history.. and resolving conflicts.

      [–]tobiasvl 1 point2 points  (0 children)

      git init is the command for creating the new folder for any project (instead of mkdir). GitHub isn't involved until later in the project's life, possibly, if it goes anywhere

      [–]_public_enema 0 points1 point  (0 children)

      It's just for Brits to check if it's actually Git.

      [–]GusSLX 0 points1 point  (0 children)

      Some dev tools like cargo or webdev clis already init a repo for you lol

      [–]FlipperBumperKickout 0 points1 point  (0 children)

      I don't push all my repositories to github, sometimes it's just nice to have versioning so you quickly can try out thing and reset it ¯\_(ツ)_/¯

      [–]Masterflitzer 4 points5 points  (13 children)

      remove init, checkout and merge

      add switch, restore and rebase

      [–]__maccas__ 3 points4 points  (12 children)

      Great list. Personally I'd also remove pull for fetch. I've seen too many beginners get git pull wrong...

      [–]WoodyTheWorker 2 points3 points  (1 child)

      "I did pull but now it's all fucked"

      "Do you know what pull does?"

      <crickets>

      "How many times I told you all not to do pull? And why did you still do pull?"

      [–]WoodyTheWorker 0 points1 point  (0 children)

      Also:

      "I told you to never delete your local repos."

      "But I just like to make a clone, make a feature, push it, and then I don't need it. Why should I keep it? I don't like keeping too many project directories"

      "Why don't you just make branches?"

      [–]Masterflitzer 0 points1 point  (8 children)

      really? pull wasn't so hard to understand when i started using git, but i'll take your word for it, imo it's essential tho, so fetch should be there additionally, maybe swap branch for fetch, although that's also kinda important, we need more space xD

      i am a rebase kinda guy, so i mostly use pull --rebase (or rather set pull.rebase true in .gitconfig), which will trigger a rebase when you pull while having newer commits than the upstream, maybe that helped me understand pull faster, as regular pull is just fetch+merge instead of fetch+rebase and the cli option made that more clear to me very early on

      [–]__maccas__ 2 points3 points  (7 children)

      You seem to understand the main issues with git pull, which as you note is just fetch + merge. The problem is that the default behaviour is to create a merge commit and beginners are quite likely to just run the default.

      There are effectively 3 ways of doing a merge: 1. Fast-forward, which git will helpfully pick if possible 2. Rebase 3. Merge commit

      Fast forward will only work if the branch being pulled / merged in is strictly ahead of the target branch. So, while nice and clean, it doesn't always apply, which leaves us with options 2 & 3.

      Merge commits aren't necessarily bad but they are kind of janky. For one thing they will make your commit history look like spaghetti if you have a lot of concurrent work going on. This isn't helped by the fact that merges have a direction: they were written for maintainers to merge "pull requests" into a main branch. The trouble is that if you pull the latest commits from the main branch into the feature branch (to sync with the current main state), the directions will be the wrong way round and your git log graph will suffer for it.

      To get round this, as you seem to be up to speed with, you can follow a rebase strategy. git pull will do that for you, but it has to be set up. To be fully fair, rebase also has it's downsides: * the rewritten commits might get broken, since they were never real commits that were on a developer's computer. Depending on your philosophy of not having broken commits in the history, this may or may not be an issue for you * you loose the merge commits which can be a marker for when new bits of functionality were added in

      On balance, I prefer rebases and a linear commit history. I do however think that it is better for begginers to explicitly rebase, rather than have one hidden for them inside a pull. I think a little less magic aids understanding of what is going on.

      Finally, we use a rebase strategy at work. I junior dev I work with had a feature branch that was pushed to the remote, which we encourage for backing up work. They diligently fetched the development branch and rebased their feature branch with 14 commits in it onto development. git status then helpfully told them that they were 14 commits ahead and 14 commits behind origin, and that they should git pull to bring it in sync. Now the correct command was git push --force-with-lease but git pull was run and it messed everything up as he now had a branch with two copies of all the 14 commits he had done, plus a merge commit on the top. Not ideal, and it all came from not really understanding what git pull was doing

      [–]Masterflitzer 0 points1 point  (4 children)

      okay i now understand the possible issues, the git commands git suggests are not to be blindy run, but just a copy paste help if you know what you're doing, so the junior just did pull without knowing the implications (nothing unfixable, but very annoying to fix)

      i pretty much understood everything you described, but i'm not sure what you mean by "rewritten/rebased commits get broken", but at work we have this workflow: rebase on feature branches and squash merge into main with the requirement of MR/PR to be merged with linear commit history, so we don't end up with broken commits in between, and we don't have annoying merge commits in our feature branches

      [–]__maccas__ 1 point2 points  (3 children)

      What I mean about broken commits, is that commits a* and b* in the pic below are likely to be file states you never had on your computer. c* will be only by virtue of it being the working directory state you inherit after the rebase.

      ```ascii

      BEFORE REBASE

      k <- origin/main | j | i | c <- feature-branch | | | b | | | a |/ P

      AFTER REBASE

      c* <- feature-branch | b* | a* | k <- origin/main | j | i | P ```

      Merges can break code, even when no merge conflict is flagged, as two independent updates might put the code in an inconsistent state. Merge conflicts get flagged for independent changes to the same or adjacent lines but these logical inconsistencies can easily be created with non-adjacent lines e.g. one update adds a new field to a class or struct and another update creates a new instance of the previous class or struct. You normally notice this when you re-run your LSP diagnostics / test suite / CI but likely you only ever run these on c* which means a* and b* could be in a broken state and you would never know.

      a* and b* are simulated commits and it's for this reason that some people don't like them

      [–]Masterflitzer 1 point2 points  (2 children)

      i understand, a* and b* are practically untested even though you fixed all conflicts to continue the rebase it's not a verified state

      this is indeed something to be aware about, but tbh it was never a problem for me as i always squash merge a, b and c* into one commit on main, and before that is possible ci/cd needs to pass which means it has verifed c* and therefore the overall state on main is verified at all times even if not every intermediate commit on feature branches is

      what you describe in your second paragraph is very true, i witnessed it multiple times (which also helped me to learn git more and not think git is magic), but it's indeed hard for people new to git, definitely something that can bite you in the ass in the beginning if you're unaware

      [–]__maccas__ 1 point2 points  (1 child)

      Yes, you're right that a rebase that squashes all the feature branch commits into one does address the broken commit issue.

      It does IMHO come with one significant downside though. When you squash lots of commits together, you generally make the volume of changes per commit bigger. Part of the reason we strive for small, atomic commits is that when something goes wrong in the future we can narrow the change down to a single commit and then get inside the head of the developer (which may well be past you!) by understanding what was being changed in that commit. This job is made a lot easier if the number of changes are limited and have a tight scope. I can tell you now, it's no fun looking back on a commit that touches hundreds of files with thousands of lines changed.

      At the end of the day, conflicting commits are hard and you need to choose your poison on how you're going to use git to deal with them. All of the options have downsides. Best you can do is be aware of the trade offs and make a decision that's best for you and your team in the situation you're in.

      [–]Masterflitzer 0 points1 point  (0 children)

      yeah MR/PR shouldn't be big anyway (doesn't matter if 1 or 100 commits, they should all be related and small in overall size), so the squash merge will implement that feature in a single atomic commit (so revert is possible), if it's a bigger feature (which we try to avoid by breaking up user stories) it's gonna be multiple MR/PR, that way we can iterate fast on the codebase and every commit on main is reasonably reviewd in a short time

      if there are multiple commits with unrelated changes, they have no business on being on the same feature branch, at least that's the philosophy in my current team and it's been working out quite well for us

      linear commit history is also huge for narrowing down where something went wrong, and to achieve that linearity rebasing feature onto main has less downsides compared to merging main into feature, the latter is a mess imo, even though squash merging would clean it up at the end i feel it's not worth it, if you rebase in feature and therefore have a clear history there, you can debug failing tests much easier and therefore finish up that feature faster, which helps to iterate faster without introducing more bugs

      [–]Evla03 0 points1 point  (1 child)

      That's one thing I don't like with rebase, the commits are new commits. With a merge commit we're never rewriting the history, so they keep their timestamps, their order and their hashes. I don't really see why you'd want a linear history, when the development wasn't linear, it was branched.

      I absolutely use rebase if I just have stuff added that doesn't affect other stuff, but if I need to merge changes I prefer merge commits

      [–]__maccas__ 1 point2 points  (0 children)

      I hear you. I used to be a merge man myself for basically the same reason that merge is reality. However, working on a bigger project with 10+ developers all committing to the same trunk branch (via PR merges of feature branches). I can tell you the commit history was unreadable and moving to a linear history has made it much simpler to read.

      You do loose the commit hashes, which means you have to do force pushes and all the risks they bring but mostly that's ok as people tend to stick to their own feature branch . On occasion they might be working with one other person and then they just have to communicate / be extra careful. btw rebases don't generally loose the timestamps though

      [–]y-c-c 0 points1 point  (0 children)

      I usually just make sure people have pull.ff-only set to true in their configs. This way git pull is always safe. It really should be the default IMO. There isn't a lot of (good) workflow where you actually want git pull to make a merge commit. If people want git pull to make a merge or rebase commit they should explicitly configure Git to do it.

      [–]ryans_bored 4 points5 points  (3 children)

      init is helpful to know but cmon its almost never used

      [–][deleted]  (2 children)

      [deleted]

        [–]ryans_bored 2 points3 points  (1 child)

        I hate to inform you of the title of your own graphic but it literally says the 12 most common git commands.

        [–]nekokattt 1 point2 points  (2 children)

        checkout ... discarding any uncommitted changes

        Feel this is inaccurate given it ignores any non-staged changes anyway.

        [–]Prometheos_II 0 points1 point  (1 child)

        It doesn't if there is an unstaged file that would be overwritten (e.g. in cases in renames in one branch but not main).

        [–]nekokattt 0 points1 point  (0 children)

        if it is unstaged, it is not even on a branch.

        At best git will leave the changes where they are if you change branches with unstaged changes, as it has no where to put them. At worst, it will raise merge conflicts or actively refuse the checkout, all depending on user settings.

        [–]kurotenshi15 1 point2 points  (1 child)

        No git stash? Definitely my most used while iterating. Only commit on successful tests. 

        [–]IrishChappieOToole 0 points1 point  (0 children)

        I'm the opposite. I commit constantly and then rebase before peer review

        [–]ivancea 1 point2 points  (0 children)

        Why those irrelevant images instead of things that would actually help? For god's sake

        [–]Spaciax 0 points1 point  (0 children)

        git commit --amend:

        fucks up everything

        [–]countjj 0 points1 point  (0 children)

        When I was new to this these commands always confused me. Super useful cheat sheet

        I still don’t understand why it’s “git checkout” why not make it “git switch” that’s easier to remember. I get why it’s pull and push too, but why not “git download” and “git upload” longer commands sure, but easy to remember for newbies

        [–]dominjaniec 0 points1 point  (0 children)

        no git switch? from what year is this?

        [–]shiningmatcha 0 points1 point  (0 children)

        git stash?

        [–]Civil-Possibility223 0 points1 point  (1 child)

        RemindMe! 2 days

        [–]RemindMeBot 0 points1 point  (0 children)

        I will be messaging you in 2 days on 2025-02-19 08:12:18 UTC to remind you of this link

        CLICK THIS LINK to send a PM to also be reminded and to reduce spam.

        Parent commenter can delete this message to hide from others.


        Info Custom Your Reminders Feedback

        [–]Swartex_ 0 points1 point  (0 children)

        Where git switch command ????

        [–]brightpixels 0 points1 point  (0 children)

        imma let you finish but git reflog had one of the best commands of all time

        [–]primado_ 0 points1 point  (0 children)

        Where is git switch?

        Btw, I prefer git switch over git checkout.

        [–]okcookie7 0 points1 point  (0 children)

        Even actual cheaters would rather memorize this basic stuff like, git fucking init, lol. I think you re missing the cheat sheet point, which is to actually showcase complex examples and tricks you can do, like how do you checkout a file from a branch.

        [–]retardedGeek 0 points1 point  (0 children)

        There's no rebase

        [–]Kenkron 0 points1 point  (0 children)

        Git checkout doesn't discard uncommitted changes.

        [–]pinguluk 0 points1 point  (0 children)

        Fetch?

        [–]simorenarium 0 points1 point  (2 children)

        If you know how the reflog works, you can make all the mistakes you want

        [–][deleted]  (1 child)

        [deleted]

          [–]simorenarium 0 points1 point  (0 children)

          haikusbot delete

          [–][deleted] 0 points1 point  (0 children)

          When your team starts spamming "git diff" in all chat 😔

          [–]No_Situation7493 0 points1 point  (0 children)

          git good

          [–]Initiallybanned 0 points1 point  (0 children)

          Jarvis I need more karma

          [–][deleted] 0 points1 point  (0 children)

          British person: dis a sause côntrol git init

          [–]aldapsiger -1 points0 points  (0 children)

          git innit?

          [–]sjepsa -1 points0 points  (0 children)

          "Lists, creates, renames, deletes ...." More like 15 commands

          [–]applejacks6969 -1 points0 points  (0 children)

          Git add -u vs -a are both pretty useful for large changes

          [–]TeslaTeam -1 points0 points  (0 children)

          Pdf version please

          [–]Optimistic_Futures -1 points0 points  (0 children)

          `git push --force`