Git

Rebasing

To Ancestor

You created a sequence of commits, feat-1 and feat-2, and you realize "hrmm, feat-2 doesn't actually depend on feat-1. I could make both of these PRs against main at the same time."

Given a repo like this:

git init
touch A && git add A && git commit -m "A"
touch B && git add B && git commit -m "B"
git checkout -b "feat-1"
touch C && git add C && git commit -m "C"
git checkout -b "feat-2"
touch D && git add D && git commit -m "D"
gitdot --png
RebaseToAncestor1.png

You want feat-2 and feat-1 to both be based off main. You can't just rebase feat-2 on main:

git checkout feat-2
git rebase main
gitdot --png
RebaseToAncestor2.png

Nothing happens, because a rebase replays commits onto the new base, and in this case that would just replay the feat-1 and feat-2 commits on main again (changing nothing).

Solution: only reply the feat-2 commit on man using --onto with a cut-point! The form of the command is git rebase --onto <Target> <Cut Point>.

git checkout feat-2
git rebase --onto main feat-1
gitdot --png
RebaseToAncestor3.png

To Specific Commit

Given a repo like this:

git init
touch A && git add A && git commit -m "A"
git checkout -b "feat-1"
touch B && git add B && git commit -m "B"
git checkout main
touch C && git add C && git commit -m "C"
touch D && git add D && git commit -m "D"
gitdot --png --msg
RebaseToCommit1.png

Rebasing feat-1 on main would make B point to D. But say we wanted to rebase B on to C instead?

git checkout feat-1
git rebase --onto C

I Accidentally…

Commited to the Wrong Branch

You locally committed to main instead of develop, but you haven't pushed the commit yet.

  • Soft-reset main back to origin/develop
  • The committed changes will now be staged
  • Switch to develop branch
  • Commit staged changes
git reset --soft origin/develop

Rebased Remote Branch

You rebased, and then remembered that the branch you were on has already been pushed to a remote repo. You shouldn't push this rebase!

  • You need to return the current branch to where it was before the rebase.

Ammended Remote Commit

You ran a git commit --ammend, then realized you've already pushed the commit you were ammending! You can't push the ammended commit! (You can, but that would be rewriting history on the remote server).

  • Reset soft to origin/<branch-name>. Then the changes you just committed will be staged.
  • Commit again as a new commit.

Committed Locally Before Pulling Remote

Somebody pushed changes to your branch while you were still committing locally. Now your branch and the remote branch have diverged.

  • Option 1: Merge remote into local. Just do a git merge of remote/branch-name.

Fixup

You make a change that ought to have been part of a previous commit, but that previous commit is not the last commit (so you can't just --amend the last commit).

First, make this new commit and mark it as a fixup of the offending commit:

git commit --fixup fb2f677

Next, run a rebase --squash, which does a rebase where fixup commits are squashed into the commits that they fix up. The commit hash is the commit where the rebase is started from.

git rebase -i --squash ac5db87

Refs

HEAD

HEAD is usually a symbolic ref, meaning it points to another ref. The exception is when you are in a detached HEAD state, in which case HEAD points directly to a commit.

git symbolic-ref HEAD

Some ways to print the SHA of the HEAD ref:

git rev-parse HEAD
git log -1 --format='%H'

Branches

Current branch name:

git rev-parse --abbrev-ref HEAD

Diff

Diff a branch with the commit you branched from.

git diff my-branch...main

First Commit

Some ways to find the first commit in a Git repository:

git show $(git rev-list --max-parents=0 HEAD)
git log --reverse

Latest commit

Fun way to get the latest commit hash on a repo! (Or really, any commit hash for any ref). You don't even need the repo cloned locally.

git ls-remote \
    https://github.com/cfclrk/rules_clojure.git \
    refs/heads/master

Files changed in commit

git diff-tree -r --no-commit-id \
    --name-only $sha

Tags

Get Project Version From Tag

I got this from cider here.

git describe --tags --abbrev=0

Is HEAD a tag?

If HEAD is a tag, this will print the tag name. If not, it prints nothing.

git tag --points-at HEAD

List Tags Matching Pattern

git tag --list "v*"

Project Root Dir

git rev-parse --show-toplevel

Tools and Resources