Git

commit

When running git commit without a flag, add the Co-authored-by trailer to credit collaborators:

Co-authored-by: Collaborator Name <collaborator@example.com>

This goes at the bottom of the commit message body, separated by a blank line. GitHub will recognize both authors in the commit.

Squash commits before merging a PR to keep the history clean - one commit per logical change is easier to review and revert:

$ git rebase -i HEAD~3

This opens an editor where you mark commits to squash or fixup into the first one.

rebase workflow

To rebase your feature branch on top of the latest main:

$ git fetch origin
$ git rebase origin/main

If conflicts arise, resolve them file by file, then:

$ git add <resolved-file>
$ git rebase --continue

To abort a rebase that went wrong:

$ git rebase --abort

useful commands

Show what changed between your branch and main (useful before opening a PR):

$ git log --oneline origin/main..HEAD
$ git diff origin/main...HEAD --stat

Amend the last commit message (only if not yet pushed):

$ git commit --amend

Unstage a file without losing changes:

$ git restore --staged <file>

View the history of a specific file:

$ git log --follow -p -- <file>

Search commit messages for a keyword:

$ git log --grep="iscsi" --oneline

comparing branches: .. vs ...

git diff A..B shows the difference between two points - and what A and B are changes the meaning completely:

$ git diff origin/main..HEAD       # everything your branch adds vs main (before opening a PR)
$ git diff origin/my-branch..HEAD  # what changed since your last push (before force-pushing)
$ git diff main..my-branch         # two local branches

Double dot is a direct diff between the two refs. Triple dot diffs B against the common ancestor of A and B - handy for git log to list only the commits on your branch, even if main has moved on:

$ git log origin/main...HEAD --oneline

For git diff, double dot is almost always what you want.

editing an earlier commit

To amend a commit that isn’t the latest, mark it edit in an interactive rebase:

$ git rebase -i HEAD~3      # go back N commits

In the editor: pick keeps a commit as-is, edit stops so you can amend, squash merges into the previous commit, reword changes only the message. When the rebase stops on an edit:

$ # make your changes
$ git add <files>
$ git commit --amend -S --no-edit
$ git rebase --continue

If you have unstaged work when starting, git stash first and git stash pop after the rebase stops.

force-push safety

Rebasing rewrites commit hashes, so updating the remote needs a force push. Always prefer the safe variant:

$ git push --force-with-lease   # refuses if someone else pushed since your last fetch
$ git push --force              # overwrites unconditionally - avoid on shared branches