Learn Git Branching

(learngitbranching.js.org)

259 points | by dsego 122 days ago

18 comments

  • l5870uoo9y 121 days ago
    I have these Git shortcuts stored in my .zshrc file:

    # https://stackoverflow.com/questions/4298960/git-add-a-git-co...

    git config --global alias.ac '!git add -A && git commit'

    git config --global alias.acm '!git add -A && git commit -m'

    git config --global alias.ll '!git log --graph --full-history --all --color --pretty=format:"%x1b[31m%h%x09%x1b[32m%d%x1b[0m%x20%s"'

    git config --global alias.gst '!git status'

    git config --global alias.gca '!git commit -a --amend'

    git config --global alias.gp '!git push origin HEAD'

    git config --global alias.bd '!git branch -d'

    git config --global alias.bdd '!git branch -D'

    git config --global alias.mc '!git diff --name-only --diff-filter=U'

    git config --global alias.co '!git checkout'

    git config --global alias.po '!git push origin'

    git config --global alias.cp '!git add -A && git commit -m "Content" && git push'

    Out of those, I only – but often – use `git acm` and `git co` and `git po`.

    Edit: formatting (oh dear).

    • samatman 121 days ago
      I went to the other extreme. I have a somewhat elaborate fish function which detects any use of -a and refuses to call Git with it.

      I had gotten lazy about good commits, basically, `git commit -a` was in my muscle memory. Forcing myself to treat staging as a step before committing, and getting in the habit of using `git add -p` when called for, has made a meaningful difference in the quality of my commit history.

      If one is disciplined about making changes in the first place, I suppose this matters less. But I'm not, and also don't want to be: if I spot something else which needs doing while I'm in flow mode, I'm not going to put it off just to keep commits nice, not when there are better options for doing that part.

      • alkonaut 121 days ago
        I make one elaborate commit where I carefully stage things etc. But invariably I miss something, find 3 typos and so on.

        that’s when the aliases are handy. The alias for fixing a typo is using all, amend and no-edit. Super useful. But obviously needs to be used with care when you only do such a fix. I’d be perfectly happy for my git oops to refuse running if there is more than one changed line to commit, for example.

    • Cthulhu_ 121 days ago
      My git status is `git s` aliased to `git status -sb`, don't need all the extra clutter.

      I also have a few .zshrc aliases, like `gap` which is `git add -p` or `gam` which is `git commit --amend --no-edit`.

      But also some typo fixing ones that I end up doing a lot, some because I type in half a command and get distracted / check something, then start over with a partially filled in command:

          alias gigit="git"
          alias gitgit="git"
          alias ggit="git"
          alias qgit="git"
          alias ght="git"
          alias got="git"
          alias cleear="clear"
          alias gits="git s"
      
      Another trick, in your .gitconfig, add this [alias]:

          git = !git
      
      That way, if you accidentally do `git git commit` or whatever (like above) it just works. Also, some typo fixes there:

          commmit = commit
          comit = commit
    • cryptonector 121 days ago
      I have aliases like

        - `ls` for `ls-files`
        - `lo` for `git log --oneline`, then add a variety of letters in multiple combinations like
          - `r` for `--reverse`,
          - `s` for `--stat`,
          - `om` for `origin/master..`,
          - `1` for `-n1`, `2` for `-n2`, `3` for `-n3` (maybe up to 5),
      
        - `rbi` for `rebase -i`,
        - `rbiom` for `rebase -i origin/master`,
        - `rbc` for `rebase --continue`
        - `rba` for `rebase --abort`
      
        - `cp` for `cherry-pick`,
        - `cpa` for `cherry-pick --abort`,
        - `cpc` for `cherry-pick --continue`
      
      I also have _shell_ aliases like `gits` that works out to `git status -uno`, and `gita` that lets me add tracked files. I need a `gitca` for `EDITOR=true git commit --amend`.

      Notice I don't have aliases for merging. I use strictly rebase and cherry-pick workflows.

    • drmohundro 121 days ago
      I found these online and added them to my gitconfig at one point... I can't take credit for them. Integrating fzf with git makes working with branches even better (with fuzzy matches for checking out as well as deleting branches)...

      cob = !BRANCH=`git recent --no-color | fzf` && git checkout ${BRANCH}

      db = !BRANCH=`git branch --no-color | fzf` && git branch -d ${BRANCH}

      dbf = !BRANCH=`git branch --no-color | fzf` && git branch -D ${BRANCH}

    • tecoholic 121 days ago
      I so want to do this, but I am always scared that I will accidentally put in a wrong character and do something that will cause me hours of rework.

      Sure question, why have it in zshrc and load it every time a new shell starts? It’s all in the .gitconfig after the first run anyways right?

      • actinium226 121 days ago
        Don't live in fear. Embrace the chaos.
    • alkonaut 121 days ago
      It’s strange that git aliases can’t accept multiple git commands. It gives a strange divide between single command aliases which go in your git config and multi command aliases which go in a shell config. Shells vary and some don’t even have aliases (cmd is probably the most used shell of them all and it - almost - doesn’t have aliases at all).

      Being able to define an && alias in .gitconfig would be great for being able to share snippets like these across shells and OS:es.

      My most used alias is by far “git oops” for git commmit —all —-amend —-no-edit

      • stephenr 121 days ago
        You can absolutely put multiple commands in a single git alias. The comment you replied to even shows it.

        There's no reason any of those couldn't be in a .gitconfig file.

        Prepend the alias with ! it's executed via a shell. I use it to embed shell functions (which generally call git)

        Eg an alias to show commits missing (useful when using cherry pick)

            show-missing-commits = "!show_missing_commits() { local target=\"${2:-$(git rev-parse --abbrev-ref HEAD)}\"; printf -- 'Showing commits present in \"%s\" but not \"%s\"\n' "$1" "$target"; git log --no-merges \"$1\" ^\"${target}\"; }; show_missing_commits"
        • alkonaut 121 days ago
          I’m pretty sure that it’s shell specific whether it works precisely because it defers it to the shell. For windows cmd in particular it’s… not great.

          What I mean is I’d like git to do it natively. But git was written assuming a posix shell and it shows up here and there sadly.

          Example:

             git command1 —-flag1 <some_separator> command2 —-flag2
          
          Should be possible for git to simply interpret sequentially.
          • stephenr 121 days ago
            > But git was written assuming a posix shell and it shows up here and there sadly.

            Given that the entire point of POSIX is cross platform compatibility, this is one of the few times when I will say they (Linus/git) got it 100% right.

            > Should be possible for git to simply interpret sequentially.

            Simply? What should it do if the first command fails? Should it exit early? What if you want it to run the second only if the first returns error? What if you want to pipe the first to the second? Or use it as an argument?

            Aliases have a "simple" mode where they call exactly one thing. Anything beyond that can't be simple, without also being useless.

            If Microsoft can't ship a POSIX compatible shell that's on them, and if you insist on using an OS that doesn't have a POSIX compatible shell, that's on you.

            I don't start Linux and ask why it doesn't run whatever people use Windows for... malware I assume?

            • alkonaut 121 days ago
              > Simply? What should it do if the first command fails? Should it exit early? What if you want it to run the second only if the first returns error?

              You’d eventually reinvent a shell. But for just these 2 cases you’d just need 2 separators analogous to the & and && of the shell. But yeah it gets messy if you wanted to run a non git command in the middle.

              The thing is, all those !a && b are perfectly valid in both posix and cmd. It just refuses to do the whole “if it starts with ! call the shell”. I guess all that’s missing is that it just needs to execute it. That would probably be the less complicated change. That at least doesn’t seem like a big controversial addition (maybe this is already done now it was a couple of years since I tried and hit a brick wall with aliases under cmd)

      • OJFord 121 days ago
        More than that, it can run arbitrary non-git commands too: prefix with `!` to run in the shell instead of as arguments to `git`.
    • hnben 120 days ago
      you should add a dog for pretty logging

          git config --global alias.adog "log --all --decorate --oneline --graph"
      
      https://stackoverflow.com/a/35075021
  • TheHippo 121 days ago
    Nothing about the content of the site, but please stop determining the language of the user based on geolocation. If the browser says he wants English, serve English.
    • xxbondsxx 119 days ago
      Hello, author here :) It's actually not based on geolocation, I just directly look at navigator.language / navigator.browserLanguage: https://github.com/pcottle/learnGitBranching/blob/main/src/j...
    • Filligree 121 days ago
      If the browser says English, chances are pretty good that's because the user didn't configure it.

      ... that's the explanation I've heard, but I agree that it's obnoxious.

      • troad 121 days ago
        That just doesn't seem to follow for me. Aren't most browsers auto-configured to request the language they were configured in? And don't most browser installs / setups default to the language of the user's system?

        I feel like if the browser requests English, odds are overwhelming that the user wants English. Assuming otherwise strikes me as someone trying to get a bit too clever with their heuristics. (And ending up in a worse place than if they hadn't bothered - location is a terrible, terrible heuristic for language.)

        I think the real issue here, though, is that the language button at the bottom of this page isn't interactive until several dialogues have been clicked though. Dialogues that are likely totally incomprehensible to the person trying to change language.

        • Filligree 120 days ago
          > Aren't most browsers auto-configured to request the language they were configured in?

          This may be true now. It was less true in 90s, where this logic originated.

          • troad 120 days ago
            I don't recall browsers behaving all that differently in the 1990s. Per W3, re MSIE 4.0, in 1997:

            > The Accept-Language header defaults to the current System User Locale ID but can be overridden by user.

      • Double_a_92 121 days ago
        Usually browsers automatically configure themselves according to the operating system language (I guess).
  • leetrout 121 days ago
    I also recommend an alias like this (and l5870uoo9y has other examples and how to set them as git aliases)

      alias gitlg="git log --graph --all --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(bold white)— %an%C(reset)' --abbrev-commit"
    
    From http://stackoverflow.com/questions/1057564/pretty-git-branch...
    • TomK32 121 days ago
      tig is a really nice tool for a very similar view https://jonas.github.io/tig/
      • petepete 121 days ago
        I use tig for staging individual lines and hunks. Haven't found a faster or easier way in more than a decade. Such an underrated program.
        • rgoulter 121 days ago
          Tig looks like a wonderful tool for all kinds of git interaction.

          I'm not too familiar with tig, but it looks like it does a lot of what Emacs' magit does.

          AFAICT, there are some things I find useful in magit that tig doesn't have:

          e.g. magit allows for committing/amending/rewording with just a few strokes; afaict, tig doesn't support the equivalent out of the box.

          e.g. in magit's status view, you'll get to see the diff for each change alongside the status entry. (Whereas tig limits itself to one view at a time; and viewing the diff is a different view than the status view).

          • petepete 120 days ago
            Tig is simple by design and it has some features I find useful that magit doesn't have.

            1. starts instantly

            2. requires no plugins or config (or an entire editor/os!)

            3. available in every distro's repo

            4. lets me stage and unstage lines or hunks with 1 key

          • arunix 121 days ago
            There's also lazygit which does have features like this

            https://github.com/jesseduffield/lazygit

  • talkingtab 121 days ago
    I'm one of those "I don't want to read the manual" people and have long used git. However, this was very helpful to easily understand some cargo-cult things I was doing. Shame on me! And thank you!
  • actinium226 121 days ago
    It seems whenever I show someone this site, they're like "oh this is cool!" and never look at it again.

    I wonder if there's something about it that makes it forgettable? I don't know if I can present it any differently, maybe there's a better way of presenting it to make it more appealing.

    • enriquto 121 days ago
      Maybe explaining why would you ever want to have branches in your code? I'm a longtime git user and never felt a need for them.
      • executesorder66 121 days ago
        I'm curious, are you a solo dev, or do you work on a team where multiple people are working on different features at the same time?
        • enriquto 121 days ago
          Mostly solo dev. I only use branches when working with some colleagues who are "branchists". It's not that I don't understand branches, but they always look unnecessarily cumbersome to me. The worst offenders are "feature branches"... I want my tests to try the program with/without the new feature and compare the results. Thus any feature is better implemented in the master branch, and activated using a flag. No need for a branch. If your feature is hidden in another branch, how can you test it? Does your test suite run git commands?
          • actinium226 120 days ago
            Do you push your code to a repository where you have some CI set up? Or is it just local? If it's the latter and you just run tests on the command line then yea maybe you don't need branches. On the other hand I find branches can be useful if I have a larger refactor going on and I'm not too sure what it's going to look like in the end. Then I can keep it on a branch and compare it to "last known stable commit," and if something else comes up that needs to be fixed right away I can put my larger refactor on hold and fix it, and come back to the larger refactor.
    • commandersaki 120 days ago
      Yeah that's the situation I keep running into.

      Them: "I really want to be proficient in git." Me: "Here's the tutorial site that I used that helped me grasp concepts and be more adept." Them: "Cool thanks."

      And of course they never put in the time or bother with the site ever again, repeat ad-nauseum.

  • cryptonector 121 days ago
    Git _is_ branching. Every clone is a[n unpublished] branch.
    • dailykoder 121 days ago
      For me, git is a prime example which shows that theoretical understanding of some problems is absolute beneficial. If you just keep in mind that it's (basically) just a directed graph, it makes the usage a lot easier.
    • derriz 121 days ago
      I dunno - for me git doesn't really have branches. What it calls "branches" could more accurately be called version/commit pointers.

      A "branch" traditionally in SCM was a sequence of versions/commits. In those SCMs, the "main" (or master) branch was an identifiable sequence of versions/commits.

      There isn't a "main" branch in git, there's a current "main" version/commit. If your history is fully linear, a pointer to the "main" commit allows you, via parent following, to discern a "main" branch. But if you've been using merge, for example, there isn't a unique path from the current main commit to the root.

      • User23 121 days ago
        Understanding refs is key to understanding Git. It’s not really difficult and a lot of mystifying cargo cult stuff suddenly becomes intuitive once you realizing you’re pretty much just manipulating pointers into a directed acyclic graph where each node has between 0 and 2 parents.
        • Zambyte 121 days ago
          0 and >0 parents technically, but in practice usually between 0 and 2. Merge commits (commits with >1 parent) can have more than 2 parents.
      • cryptonector 121 days ago
        A proper branch in Git is just a name for a commit with the rule that when you commit to a branch you move the name to point to the new commit, and similar rules on push.
      • PhilipRoman 121 days ago
        This does sometimes cause problems when trying to understand history. Would love if git recorded the current branch name (if any) somewhere in the commit
        • imron 121 days ago
          I have a git commit hook that automatically adds a git trailer containing the jira ticket/issue number to the commit message.

          It gets this from the branch name which by my convention always contains the ticket ref.

          It would be trivial to modify this to use the full branch name instead.

          • imron 121 days ago
            And now I'm on my computer, here is the modified version. Put this in:

            .git/hooks/prepare-commit-msg

                #!/usr/bin/env bash
            
                BRANCH=$(git symbolic-ref --short HEAD 2> /dev/null)
            
                # fail gracefully if we don't have a branch
                if [ -z "$BRANCH" ]; then
                    exit 0
                fi
            
                # add a git trailer with the branch
                git interpret-trailers --in-place --where before --if-exists addIfDifferent --trailer Branch="$BRANCH" "$1"
            
            And if you add the following alias to your ~/.gitconfig you'll be able to see the branch names in the logs by typing git lb (for log branch):

                [alias]
                    lb = log --color --graph --pretty=format:'%Cred%h%Creset %C(bold)%(trailers:key=Branch,valueonly,separator=%x2c)%Creset - %s %Cgreen(%cr)%C(bold blue) <%an>%C(nobold yellow)%d%Creset' --abbrev-commit
            • cryptonector 120 days ago
              I'm thinking of writing a commit hook to make sure that every commit has the following metadata in trailers:

                - original parent commit
                  
                  Between this and the synopsis it
                  should then be possible to find all
                  evolutions of this commit.
                
                - original upstream URI and branch (if
                  known) or upstream URI and branch
                  targeted for integration (prompting
                  if necessary)
                
                - commit type and optional tracker ID
              
              When a commit has these metadata already, leave it.
      • Zambyte 121 days ago
        Jujutsu is a great interface to the git data structures, and it makes all of this obviously true.
      • dahart 121 days ago
        Meh getting too reductionist doesn’t necessarily help anything. Branching is a concept, not an implementation detail. There is a “main” branch in git because that’s how people talk about it, and what they usually mean when they refer to “main” or another branch; people are not usually referring to a singular tip-of-the-branch commit, but the whole branch and it’s history. A branch is different from a tag or a commit because the branch is expected to move frequently (point to different commits) and tags/commits aren’t. The implementation isn’t different, but the usage and meaning is. Branches in other SCMs are really no different conceptually. Perforce doesn’t identify any unique path from the tip of the branch to the root it came from, you can merge both directions. Same is true in other SCMs.
        • derriz 121 days ago
          It's more than an implementation detail.

          Without changing the DAG, I can arbitrarily move "main" to point at any commit I like in git. This isn't some esoteric action - this is done constantly as part of normal git workflow.

          While what people understand as a branch in traditional SCM discussions can only grow by the addition of new versions. Commits/versions belong to a specific and unique branch and once added to the version tree, cannot "move" branches.

          While in git, a commit can belong to any number of (git) "branches" which makes presenting a true branch based history (if the DAG contains merges) impossible. Which is why we all end up using a workflow based on rebase instead of one based branch-and-merge.

          I've observed how much confusion using the name "branch" for what is a version pointer in git causes to those starting with git. The easiest way I've found to help people is to tell them forget about the word "branch" and think in terms of the DAG and pointers to elements in the DAG.

          • dahart 121 days ago
            Sure git is a bit different than P4 or Svn or whatever; they all have unique implementation details. I don’t disagree that git confusion sometimes exists nor that you’ve seen it, but it might make your point clearer to give a specific example, one that knowing a branch is a pointer fixes. I’m not sure if it’s useful to think of commits as immovable; they can be rebased and cherry picked, the “version” (SHA) is another implementation detail. Moving the content of commits around is common standard practice with git. That’s a different type of move than a branch move, not what I was referring to above, but I guess I’m arguing that branching is more about workflows, conventions, and conceptual understanding than how it works technically. You’re right that it’s sometimes useful to know that a git branch is a pointer and super lightweight, but that doesn’t help you differentiate branches and tags, for example. Git has a bunch of features that are ‘just’ a pointer. Knowing that is good for advanced workflows, but not really necessary for basic version control.
            • derriz 121 days ago
              For a specific example of where choosing a name ("branch") which obfuscates the fact that you are actually manipulating a pointer, maybe consider what it means to delete a branch? Or create one for that matter.

              Same in general programming, being clear on the difference between a reference and referee is vital to proper conceptualize what the operations actually do.

              But I'm not sure even that's convincing as after using git for a while it becomes completely instinctual which is why it may not seem important to clearly identify a pointer to a commit and an actual sequence of commits related by the parent/child relationship.

              • dahart 121 days ago
                I’m not following. Creating and deleting branches is easy in git without knowing it’s a pointer. What exactly is confusing about it, and how does knowing it’s a pointer help? Creating and deleting branches in Perforce is also easy, and they’re not pointers.
          • keybored 120 days ago
            I never got deep enough into the legacy VCSs like Subversion to do things like branching. Or even the peer Mercurial. So I have no other VCS concept of so-called branching.

            I don’t get what is the problem with Git’s concept of branching. It is mutable, yes, and commits aren’t somehow marked with the information about what branch it was made on. I can understand that some might expect it to. But why is Git’s “branch” so strange that it doesn’t deserve to call itself that? All technical names are in the end synthetic.

            > Without changing the DAG, I can arbitrarily move "main" to point at any commit I like in git. This isn't some esoteric action - this is done constantly as part of normal git workflow.

            You can reset to whatever in general. But this is typically not done for the main branch. People who pull it will get an error if you rewrite it completely. Only fast-forward updates are the normal ones (when you go from an ancestor commit to a descendant).

            > While in git, a commit can belong to any number of (git) "branches" which makes presenting a true branch based history (if the DAG contains merges) impossible.

            A true branch history? When does this matter? You can see that main has commits and things are merged into it. That’s typical. Of course people can make a mess of that (too easy really). But usually you have a few immortal histories (we can call them histories if you want) and things eventually end up in them.

            > Which is why we all end up using a workflow based on rebase instead of one based branch-and-merge.

            Plenty of people use only merges. Some hate rebase. Even though they shouldn’t.

            > I've observed how much confusion using the name "branch" for what is a version pointer in git causes to those starting with git. The easiest way I've found to help people is to tell them forget about the word "branch" and think in terms of the DAG and pointers to elements in the DAG.

            In other words you explain to them concretely what it is. Yes? “Branch” is just a name.

            Is a “bookmark” in Mercurial any more obvious?

            • CRConrad 119 days ago
              > I don’t get what is the problem with Git’s concept of branching. It is mutable, yes,

              Exactly like a branch on a tree: It's a living thing, that keeps on growing. The word "branch" refers to the whole thing, regardless of where the tip of it has grown to. Utterly intuitive.

              And just like branches on trees grow at their tips, the "branch" pointer in git, where you add stuff, is the latest commit. Of course; where else should it grow?

              > and commits aren’t somehow marked with the information about what branch it was made on.

              Again, exactly like a branch on a tree: Pluck a leaf from it, and there is no sign on it to say which branch it came from. But you can look at any twig or leaf and see what it's attached to, all the way to the trunk, and where it forks off from the trunk is where the branch begins.

              (Somewhere along the length of it, you could score the branch name into the bark of the tree... Nah, analogy getting a bit too literal there. But still, totally intuitive.)

          • cryptonector 121 days ago
            Opinionated branching is super obnoxious. It's much better to let you see the truth with a thin traditional branching veneer, and you can choose to enforce "fast-forwards"ness or not.
  • hnuser123456 121 days ago
    Why do the arrows point backwards? (I get it points to the parent, but I'd rather point forward chronologically, any commit history viewers that allow this?)
    • actinium226 121 days ago
      In theory one could amend the parent commit such that its timestamp is after its child commit. In practice I do occasionally edit one of the commits in my branch if a particular change fits better with that commit (most often typos).

      As such pointing to the parent is more stable than pointing to when the parent.

  • neves 121 days ago
    This is an excellent resource. The animations really help to understand Git concepts. I successfully used it for an internal Git course.
  • codingminds 121 days ago
    Sadly I'm not able to enter any command. Even after disabling the ad blocker
    • rpigab 121 days ago
      Same here, tried clicking near the prompt, nothing. Maybe I'm bad at git, can't even perform a simple commit.

      edit: right after typing this, I clicked the title of the MacOS styled window to the left and somehow it worked, even though clicking everywhere inside it didn't.

    • __jonas 121 days ago
      I had this issue, a page refresh helped
  • srameshc 121 days ago
    This is really great resource for someone getting started to understand what's going on. I see many have issues and try out different git commands without understanding the outcome.
    • leetrout 121 days ago
      It's a really great resource to come back to for those of us who have been using git over over a decade, too!
    • shagie 121 days ago
      https://onlywei.github.io/explain-git-with-d3/ is another one that I've used to demonstrate the repository state after various operations.
    • fluorinerocket 121 days ago
      I always send this to new hires who don't know git. Pretty common in mechanical engineering
  • lainga 121 days ago
    Has anyone used `git describe` in anger?
    • benbristow 121 days ago
      Don't git describe in anger, I heard you say.
  • sakesun 121 days ago
    I've finished all levels of the tutorial several times. Yet when in real work, I'm still often struggle with git. :(
  • kwar13 121 days ago
    Love this tutorial. Thanks!
  • Gepsens 121 days ago
    Sharing a few of my own :

    # https://github.com/Igosuki/dotfiles/blob/master/git/.gitconf...

    grep = grep -Ii

    lalias = "!git config -l | grep alias | cut -c 7-"

    done = "!f() { git branch | grep "$1" | cut -c 3- | grep -v done | xargs -I{} git branch -m {} done-{}; }; f"

    assumed = "!git ls-files -v | grep ^h | cut -c 3-"

    lasttag = describe --tags --abbrev=0

    lt = describe --tags --abbrev=0

    dr = "!f() { git diff "$1"^.."$1"; }; f"

    lc = "!f() { git ll "$1"^.."$1"; }; f"

    diffr = "!f() { git diff "$1"^.."$1"; }; f"

    lb = " !f() { git branch -a | more; }; f"

    cp = cherry-pick

    st = status -s

    cl = clone

    ci = commit

    br = branch

    diff = diff --work-diff

    dc = diff --cached

    r = reset

    r1 = reset HEAD^

    r2 = reset HEAD^^

    rh = reset --hard

    rh1 = reset --hard HEAD^

    rh2 = reset --hard HEAD^^

    sl = stash list

    sa = stash apply

    ss = stash save

    logtree = log --graph --oneline --decorate --all

    lmine = "!f() { git log --branches [email protected]; }; f"

    purgeforever = "!f() { git filter-branch --prune-empty -d /dev/shm/scratch --index-filter "git rm --cached -f --ignore-unmatch $1" --tag-name-filter cat -- --all }"

    updaterefsafterpurge = "f() { git update-ref -d refs/original/refs/heads/master; git reflog expire --expire=now --all; git gc --prune=now }"

    ec = config --global -e

    up = !git pull --rebase --prune $@ && git submodule update --init --recursive

    cob = checkout -b

    cm = !git commit -m

    save = !git add -A && git commit -m 'SAVEPOINT'

    wip = !git add -u && git commit -m "WIP"

    undo = reset HEAD~1 --mixed

    amend = commit -a --amend

    wipe = !git add -A && git commit -qm 'WIPE SAVEPOINT' && git reset HEAD~1 --hard

    bclean = "!f() { git branch --merged ${1-master} | grep -v " ${1-master}$" | xargs -r git branch -d; }; f"

    bdone = "!f() { git checkout ${1-master} && git up && git bclean ${1-master}; }; f"

            pr = pull --rebase
    
    I'd advise binding things like pr, po, cp, --rebase, --continue, to keyboard shortcuts though if you are in an IDE.
  • dev1ycan 121 days ago
    great tutorial!
  • mediumsmart 121 days ago
    excellent tutorial, thank you for that.
  • DavidWoof 121 days ago
    One of my biggest pet peeves about modern development is just how many people don't know jack about git even though they use it every day. It really annoys me when I see a pull request with 20 random commits (with messages like "temp" or "checkpoint") or when people merge main into their personal feature branch instead of just rebasing their branch (yes, sometimes that's not right, but I'm not talking about the corner cases).

    I always think about using "clean up a pull request" as a fizzbuzz-ish screen in interviews. It just seems like a decent proxy for "do you care at all?".

    • actinium226 121 days ago
      Just to be clear, and you probably won't disagree with this, there's nothing wrong with commits like "temp" or "checkpoint" or "WIP" (Work In Progress). I often make these sorts of commits as I'm working on stuff.

      The issue is in submitting an MR/PR with those commits. There's an expectation among professionals that you make your work presentable before submitting it for review, although those who are new to the profession don't realize that this cleanup step is necessary (how could they? the intro courses don't teach this and they're usually struggling enough with the code).

      I just wanted to throw this comment in here in case some newbie sees this and comes away thinking "oh, I can't have 'temp' or 'checkpoint' in my commit messages"

      • taberiand 121 days ago
        I encourage developers to clean up their commits but so often they're either unduly nervous about breaking things or have a fascination with "preserving the history" (of their branch, which is not yet merged in anywhere).

        I partly blame the excessive fear mongering around rebasing, where the strict Never Rebase a Pushed Branch rule is drilled into them and they never learn why or when they can break the rule safely.

        So it's an uphill fight but I just try to teach by demonstrating, frequently, exactly how they can tidy up for the merge request.

        • magicalhippo 121 days ago
          > I just try to teach by demonstrating, frequently, exactly how they can tidy up for the merge request.

          Some recommended resources for this?

      • hyperbolablabla 121 days ago
        I couldn't care less about the commit messages on a PR, I care about the diff. as long as the messages are cleaned up in the squash and merge with main
        • OJFord 121 days ago
          If it's large I want to be able to look at the commits in isolation and understand the work in the logical chunks that made sense to the author.

          If what made sense to them is temp, checkpoint, temp, temp, undo the temp, fix test, try this, that didn't work try other thing, maybe?, temp, fix test - then I don't stand a chance. Recently I reviewed one that had multiple 'rebase' commits, I have no idea.

          • scott_w 121 days ago
            > If it's large

            Then make the PR small.

            • keybored 120 days ago
              This is a conversation that keep repeating

              - Many temp commits

              - Doesn’t matter: just squash

              - But sometimes I want to have a few distinct (isolated commits for my PR)

              - Then make the PR small

              - But I have several changes

              - Then make several small PRs

              We keep coming back to “just make more PRs”. Which is curious, given that GitHub doesn’t even support dependent PRs. The thing you need immediately when your PRs start depending on each other (like refactor X before implementing Y, where both touch the same code).

              But I can’t come to any other conclusion than that this is because of the over-focus on PR as the one and only reviewable unit. Thanks to GitHub.

              I can’t really get on the small PR train. A PR has overhead and I often want to do small changes that I only happen to notice are necessary when I am in the middle of doing that one PR.

              • scott_w 120 days ago
                Don’t make dependent PRs. Make small PRs, review, merge and deploy them before starting the next.
                • keybored 120 days ago
                  That begs other questions. The relevant reviewer isn’t available until Wednesday. Do I start on the next tasks in dependent branches? The act of making all those branches is a large part of the inherent busywork. And then I need to remember to get back to them when the reviewer gets back.

                  The benefit of all these small PRs is yet to be revealed.

                  • scott_w 120 days ago
                    Why are you starting work you can’t finish for days/weeks?
                    • keybored 120 days ago
                      Is the dialogue here that you ask questions that makes the workflow more and more constrained and then when X factors are fixed it becomes obvious that the workflow works great?

                      Of course I start on work that I (not the reviewer) can work on right now.

                      • scott_w 120 days ago
                        > Of course I start on work that I (not the reviewer) can work on right now.

                        Why do you start work you can't finish?

                        • CRConrad 119 days ago
                          Who says they can't finish it???

                          Whateverthefuck this is, discussion in good faith it is not.

            • OJFord 121 days ago
              1. I'm reading not writing.

              2. Is our 'large' the same?

              3. I'd rather the PR were whatever size it needs to be to entirely do the thing it's supposed to do (and nothing else) than conform to some arbitrary size requirement.

              • scott_w 120 days ago
                You're over-thinking it. Large PRs have a known effect of reviewers' eyes just glaze over and the PR just gets a cursory glance and LGTM :+1: on it.

                That's when you know a PR is "large."

                What's stopping you breaking such a PR into smaller chunks? Some arbitrary "it does what it's supposed to do" definition?

        • Cthulhu_ 121 days ago
          But that's the thing, in most cases there's no review step anymore after the squash/merge has been made; while it's in a branch / MR, you can still edit the commit messages and content, but in most cases the squash and merge is an atomic, unreviewed step. Of course, maybe the merge commit should be generated, or should be filled in and reviewed as part of the main code review.
          • mkesper 121 days ago
            Why is squash and merge a trend now? This destroys all advantages of git in my view.
            • keybored 120 days ago
              Yeah, I’ve noticed that many around the Internet just comment with “the squash and merge” as if it’s a given that that’s the strategy that is used (even mandated).
    • tornadofart 121 days ago
      Git rebase aka People rewriting history to make it LOOK like the commit was a compilable, working work result at a point in time even though the real history was something completely different, then f*cking up the rewriting of said history, then whining "please mighty Senior SWE heeeelp I don't know what happened please help my work is gone".

      It all has trade-offs.I prefer seeing the dirty laundry.

      • Cthulhu_ 121 days ago
        I don't; once the feature has been merged, how you got to that point is no longer relevant; it's noise. Git is a log of code changes, if you use it as a work log you're using it wrong.
        • tornadofart 121 days ago
          I don't think you're wrong. I think we have different priorities.

          And we obviously have different expectations of what should happen if the code of a certain commit is run.

      • keybored 120 days ago
        That the code is “compilable, runnable” is never a given even if you never use rebase.

        We can sling around stereotypes of people failing and doing a bad job with their strategy. Then going to cry to someone (presumably you?). But I don’t think that advances the conversation.

        • tornadofart 120 days ago
          True. And I don't think I ever claimed that never using rebase is a guarantee for anything.

          I rather wanted to point out the following: Using a commit strategy based on git rebase is neither right nor wrong. It is not even best practice IMO. It has its own footguns.

          Since the parent comment was very opinionated and cast judgement, I responded in kind.

          I have been the person crying as well as the one who solved the mess, let's not kid ourselves.

          • keybored 120 days ago
            > Since the parent comment was very opinionated and cast judgement, I responded in kind.

            Fair. :)

    • Cthulhu_ 121 days ago
      It's a mindset problem; people use git commits as the equivalent of hitting save in their editor, and they feel like they should use it as a work log, justifying their time spent or feeling like they have to demonstrate how they got to a certain solution. It's not relevant. Ego is not important. You don't need to justify your time.
    • Blackthorn 121 days ago
      I agree with you in spirit (people need to learn their tools more) but your examples...not so much. Merging main into the feature branch was the original intent on how to do it. PRs are sent the way you describe because GitHub literally offers the maintainer a squash option and it's a lot easier to review a pull request when history is unedited.
      • aulin 121 days ago
        PR should be sent to review in a state that author considers ready to merge. Assuming it will be squashed and taking that as a reason for submitting dirty history is just sloppy and unrespectful.

        Merging is for keeping track of a group of commits that has been taken from a feature branch and included in the mainline. Why would you clutter the feature branch with periodic main merges when you can cleanly rebase it and keep it tidy?

        • Blackthorn 121 days ago
          > Merging is for keeping track of a group of commits that has been taken from a feature branch and included in the mainline.

          Merging is for bringing a group of commits from branch A into branch B. It is, quite literally, the original way to perform this operation. It's not "clutter", it's a correct picture of how the code was developed.

          • aulin 120 days ago
            It's clutter because it adds no information value on a short lived branch. If it's a branch that periodically syncs with another it's ok, if you're just basing off current master for a feature branch rebase is the way to go.
            • Blackthorn 120 days ago
              I've developed branches against a moving target all the time where the moving target introduced a problem in my code that wouldn't have been found by a simple merge conflict resolution. It's much much easier to find the source of the problem when you have the real history (a merge) instead of a rewritten history (a rebase).
              • aulin 120 days ago
                You are right, makes sense.
      • 7bit 121 days ago
        > it's a lot easier to review a pull request when history is unedited.

        Ist it really? If you would See my uncleaned history, it would take you days to understand what I was even trying to do.

        Your statement seems based on the assumption that someone knows how to achieve a particular thing right from the start. But that isn't always the case and there might be a lot of different approaches until the correct or best one is found.

        Do you really want to review dead code that's somewhere in the commits of a feature branch?

        • Blackthorn 121 days ago
          Yes, I'd much rather review a PR with the full unedited history, not the history that the submitter thinks is good or pretty. You often don't know what information you need until you need it. I'll take the entire, dirty history, and once I'm done with it it can be squashed.
      • CRConrad 119 days ago
        > PRs are sent the way you describe because GitHub...

        Oh, and here I though this discussion was about git. But you're talking GitHub.

        • Blackthorn 118 days ago
          They used the phrase pull request, which means GitHub, because git does not have a concept of "pull request".
          • keybored 118 days ago
            To request a pull has been a term in git(1) since 2005.

            https://opensource.stackexchange.com/a/380/30121

            (Is this pedantic? I guess in a lot of contexts. But you talked about the original, intended way to do it. So it seems on-topic here.)

            • Blackthorn 118 days ago
              Yes, pull is old. Pull request, which the original poster was explicitly discussing, is a GitHub thing.
              • keybored 118 days ago
                I was talking about “pull request”. So is the link I used.
                • Blackthorn 118 days ago
                  Right. It discussed the command "git request-pull". I do not believe the phrase "pull request" existed until GitHub popularized it.
                  • CRConrad 118 days ago
                    > It discussed the command "git request-pull".

                    In git syntax, the command is "git request-pull".

                    What's that in natural language? Perhaps... "A pull request"?

                  • keybored 118 days ago
                    Email: Hi, please pull the latest changes from dot dot dot

                    What kind of email is that?

                    • Blackthorn 118 days ago
                      Can we take a step back here and ask what point you are trying to make?

                      A user (who wasn't you) called me out for talking about GitHub instead of git, and I said it was perfectly fair because the original discussion was specifically about sending pull requests, which is a term we only talk about in 2024 because GitHub made it a thing. Therefore, it's entirely fair to discuss it in the context of GitHub and not git.

                      Now we are five posts down into this bizarre tangent and I am unsure what point, if any, you are trying to raise here. That people now use the term pull requests when not using GitHub? I don't think I've seen it anywhere except for hosted services, but my experience is not universal.

                      • keybored 118 days ago
                        We don’t have to rehash things. Let’s chalk it up to me getting some wires crossed. (It took me until writing half of the original reply to realize. Hah!)
      • keybored 120 days ago
        > Merging main into the feature branch was the original intent on how to do it.

        Based on?

        • Blackthorn 120 days ago
          The merge script existed as far back as git 0.5. Rebase came later.
          • keybored 120 days ago
            Linus Torvalds needs the merge operation as an integrator.

            Considering the email workflow of the kernel I can’t really make sense of “intended way to do it”. For individual commits people send out patches. I’ve never seen an email thread where some merge topology is recorded: it’s just a list of patches. A straight line.

            I’m pretty sure that people used patch queues before Git (and even now with quilt). Restacking a bunch of commits on top of the mainline is the same operation as a rebase.

            I’ve certainly seen Linus get mad at another maintainer for allowing a back-merge into his history (merge main back into feature branch).

    • mettamage 121 days ago
      It seems there are no best practices here. I remember senior devs working with a gui for git that didn’t have rebase in it. I worked on the cli, whenever I mentioned that I rebased people looked at me with raised eyebrows since I was not a senior and new.
    • scott_w 121 days ago
      > It just seems like a decent proxy for "do you care at all?"

      Because the reality is, when it comes to Git history, no, I don't care in the slightest. I get all the information I need by:

      - Reading previous PRs (the final diff)

      - Checking the name on a git diff of a line

      - The ticket reference

      Git commits are a tool to help me write code and reverting to a "known-good" state. Once it's merged into master/main, I don't care how messy it is because 99.999999% of the time, I'll just go back to the merge commit.

      • keybored 120 days ago
        One of the nice things about Git is that it is a fast (not just local but fast—these people care about speed) lookup program for all things relating to the code. I want all immediately useful code information inside Git. Because then I look it up quickly. Unlike having to go to at least two different web applications (PRs and issue tracker) and find the info there, often in an inferior and more convoluted format.

        But it takes surprisingly little to sell back centralization and lock-in to developers, even when working on top of a decentralized tool.

        • scott_w 120 days ago
          > But it takes surprisingly little to sell back centralization and lock-in to developers, even when working on top of a decentralized tool.

          I don't care about centralisation / decentralisation in my work. What I care about is that I have the information I need to do my job.

          > Unlike having to go to at least two different web applications (PRs and issue tracker)

          PR descriptions can be part of your merge commit message so I don't know why you need to go to a web application if you don't want to. You can also read the full diff in git diff so I don't really see what you're upset about.

          • keybored 120 days ago
            > You can also read the full diff in git diff so I don't really see what you're upset about.

            Since you don’t care about what I care about I have nothing to be upset about.

            • scott_w 120 days ago
              I care about getting things done. You seem to care about making git commits look pretty. I know what my customers prefer.
              • keybored 120 days ago
                I thought I was ending this on an amicable note.
          • CRConrad 119 days ago
            > I don't care about centralisation / decentralisation in my work.

            Yeah, that's why it's easy to sell centralisation to lots of people.

    • mandeepj 121 days ago
      > It really annoys me

      Have you set those expectations with your team or the people you are working with? Your ‘style’ shouldn’t just show up in reviews

    • McBun 121 days ago
      From my experience, nobody teaches you Git properly in school. And once you get your first job, it is too late.

      We had various lectures on languages models, math, algorithms, networking... absolutely nothing on git (I did my classes between 2008 and 2013, things might have changed now)

    • lerax 121 days ago
      same case here. almost new guy on my team we need to train them because basically just know to use what vs code exposes as git interface and some of them can replicate the same flow on terminal. rebase? that's a forbidden command for them, while our entire company use this as daily basis.

      that just kinda sad. hopeless.

    • Cthulhu_ 121 days ago
      It's so frustrating. We had a reasonably okay process going on, but then another team started working in our repository on their feature completely ignoring the fairly lightweight commit message format we ask them to uphold, and they were like "doesn't matter, we'll squash merge it". But it's not even about the commit messages; it's about the workflow they have behind it, where they commit and push as often as they hit ctrl+s, which to me communicates they're just hacking around until it works.

      A commit should be atomic; it should be a complete change with code and tests all adjusted, and ideally a message explaining what and why it changes. (In practice / in my line of work this doesn't happen because it's all front-end code implementing some poorly documented user story in jira, but hey).

      If I'm ever involved in hiring again I'll add git usage to my list of criteria. Along with whether they can actually touch type. I can't believe that the standards have dropped so far that basic computer skills are no longer necessary apparently.

    • keybored 120 days ago
      I share your frustration, personally. But unfortunately Git is more user-unfriendly than it needs to be, and doing things the proper way tends to get you into a rabbithole.

      Like merging main back into your feature branch: just rebase. But then you often need to re-do conflict resolution. You have git-rerere but, eh, it’s not discoverable at all. But let’s say you get over that hurdle. Now the next obstacle is the “never rewrite shared history”. And if you’re in a “corporate” environment chances are that you publicize your branch when you make a PR. And it can take a few days to get approval.

      Now I care. But sometimes I have doubts about whether the caring is well-founded. Exactly because sometimes people around me seem to care not one bit.