[HN Gopher] Learn Git Branching
       ___________________________________________________________________
        
       Learn Git Branching
        
       Author : dsego
       Score  : 136 points
       Date   : 2024-09-15 11:14 UTC (11 hours ago)
        
 (HTM) web link (learngitbranching.js.org)
 (TXT) w3m dump (learngitbranching.js.org)
        
       | l5870uoo9y wrote:
       | 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).
        
         | cryptonector wrote:
         | 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.
        
         | samatman wrote:
         | 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 wrote:
           | 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.
        
         | alkonaut wrote:
         | 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 wrote:
           | 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 wrote:
             | 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 wrote:
               | > 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 wrote:
               | > 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)
        
         | drmohundro wrote:
         | 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}
        
           | gcarvalho wrote:
           | There's fzf-git.sh [1], which is a collection of bindings to
           | invoke fzf for things like branches, commits, tags, etc. I
           | use it pretty often.
           | 
           | [1] https://github.com/junegunn/fzf-git.sh
        
       | srameshc wrote:
       | 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 wrote:
         | 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 wrote:
         | https://onlywei.github.io/explain-git-with-d3/ is another one
         | that I've used to demonstrate the repository state after
         | various operations.
        
         | fluorinerocket wrote:
         | I always send this to new hires who don't know git. Pretty
         | common in mechanical engineering
        
       | leetrout wrote:
       | 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 wrote:
         | tig is a really nice tool for a very similar view
         | https://jonas.github.io/tig/
        
       | cryptonector wrote:
       | Git _is_ branching. Every clone is a[n unpublished] branch.
        
         | dailykoder wrote:
         | 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 wrote:
         | 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.
        
           | PhilipRoman wrote:
           | 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 wrote:
             | 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.
        
           | User23 wrote:
           | 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.
        
           | dahart wrote:
           | 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 wrote:
             | 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 wrote:
               | 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.
        
               | cryptonector wrote:
               | 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.
        
           | Zambyte wrote:
           | Jujutsu is a great interface to the git data structures, and
           | it makes all of this obviously true.
        
           | cryptonector wrote:
           | 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.
        
       | lainga wrote:
       | Has anyone used `git describe` in anger?
        
       | talkingtab wrote:
       | 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!
        
       | mediumsmart wrote:
       | excellent tutorial, thank you for that.
        
       | dev1ycan wrote:
       | great tutorial!
        
       | DavidWoof wrote:
       | 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?".
        
         | Blackthorn wrote:
         | 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.
        
       ___________________________________________________________________
       (page generated 2024-09-15 23:00 UTC)