# Git
## Installation
### Install Git on Ubuntu or Debian
```bash
sudo apt install -y git
```
### Set up ssh keys for remote instance
Make sure that the ssh keys are password-protected
(On remote instance)
```bash
# Ensure .ssh folder has been created
mkdir -p ~/.ssh
```
(on home machine)
```bash
# Copy over ssh key (it's password protected)
scp ~/.ssh/id_rsa <USERNAME>@<IP_ADDRESS>://home/<USERNAME>/.ssh
scp ~/.ssh/id_rsa.pub <USERNAME>@<IP_ADDRESS>://home/<USERNAME>/.ssh
```
## Usage tips
### [Push only a single commit to remote branch](https://www.dennisrobinson.name/blog/push-only-one-commit-with-git/)
Useful for triggering CI to run on each commit, instead of only on the last commit pushed in a batch
Syntax: `git push <remote> <commit>:<remote-branch>`
```bash
git push origin 2dc2b7e3:master
```
If you need to also create the remote branch while pushing only one commit to it:
```bash
git push origin ac1420e:refs/heads/<mybranch>
```
### Use `delta` as git diff pager when viewing PR diffs
```bash
gh pr checkout 1835
PAGER=delta gh pr diff
DD PAGER=delta gh pr diff # Using DD alias in common
```
### [Recover git message if commit fails for some reason](https://unix.stackexchange.com/questions/590224/is-git-commit-message-recoverable-if-committing-fails-for-some-reason)
Useful if e.g. you forget to plug in your security key for [[GPG]] signing
```bash
cat "$(git rev-parse --git-dir)/COMMIT_EDITMSG)"
```
### [Reorder the last two commits on the current branch](https://stackoverflow.com/questions/33388210/how-to-reorder-last-two-commits-in-git)
Run
```bash
git rebase -i HEAD~2
```
Then change e.g.
```text
pick f4648aee My first commit
pick 00adf09a My second commit
```
to
```text
pick 00adf09a My second commit
pick f4648aee My first commit
```
Save, resolve any conflicts, and done!
### Using fixup commits to implement PR review feedback (for PRs on [[GitHub]])
**Creating the fixup! commit**
- `git commit --fixup=<commit>`: creates a "fixup!" commit which changes the content of `<commit>` but leaves its log message untouched.
- `git commit --fixup=amend:<commit>`: is similar but creates an "amend!" commit which also replaces the log message of `<commit>` with the log message of the "amend!" commit.
- `git commit --fixup=reword:<commit>`: creates an "amend!" commit which replaces the log message of `<commit>` with its own log message but makes no changes to the content of `<commit>`.
**Squashing fixup commits into their 'parent' commits**
```bash
grbi --autosquash HEAD~6
grbi --autosquash 25ea746^
```
**Rationale**
- "these kind of tiny commits, esp. in response to PR review, i like to make fixup commits. like `git commit --fixup=HEAD~3` or w/e."
- "it makes the same commit as before, but when you rebase git will automatically squash those fixup commits into the parent commit"
- "it's also nicer than just rebasing in-place and force-pushing, since then reviewers on github can't see the separate change. but that's also only because github sucks lol"
**More info**:
- (Blog post) [GIT tip : Keep your branch clean with fixup and autosquash](https://fle.github.io/git-tip-keep-your-branch-clean-with-fixup-and-autosquash.html)
### [Stash untracked files](https://stackoverflow.com/questions/835501/how-do-you-stash-an-untracked-file)
```bash
git stash --include-untracked
# Shorthand
git stash -u
```
### Create and checkout branch in one command
```bash
git checkout -b <branch_name>
```
- The [[Oh My Zsh]] git plugin has `gcb` aliased to the above
### Modify a specific commit (not the one at `HEAD`)
https://stackoverflow.com/questions/1186535/how-to-modify-a-specified-commit
1. Stash changes `git stash`
2. `git rebase --interactive 'bbc643cd^'`
Note the caret ^ at the end of the command, because you need actually to rebase back to [the commit __before__ the one you wish to modify](https://stackoverflow.com/questions/1955985/).
3. In the default editor, modify `pick` to `edit` in the line mentioning `bbc643cd`.
4. `bbc643cd` is your last commit and you can now make your changes and amend it:
- `git stash apply`
- `git add .``git commit --amend`
5. Continue the rebase: `git rebase --continue`
### Reset last commit back into staged changes:
```
git reset --soft HEAD~1
```
### Partially merge a PR to see better diffs
(e.g. if an early commit moved a file)
- DO NOT cherry pick the commits
- Go to the feature branch, checkout the last commit you want to merge
- Create a temp branch for it
- Switch to master
- Merge the temp branch into master
- Copy the feature branch into a fresh branch (same commits but different PR diff)
- On Github, replace the old PR with a PR from the fresh feature branch
- As an example, this occurred with Eutykhia #18
### Revert a commit not at `HEAD`, but don't commit
https://stackoverflow.com/questions/2318777/undo-a-particular-commit-in-git-thats-been-pushed-to-remote-repos
```bash
git revert -n d09b3810
```
- `-n` switch prevents it from committing
### Creating and pushing tags for minor releases
https://stackoverflow.com/questions/5195859/how-do-you-push-a-tag-to-a-remote-repository-using-git
```bash
git tag v0.4.1
git push origin v0.4.1
```
**NOTE:** For larger releases, it is better to create the tag / release using the UI on [[GitHub]], as you can auto generate a large part of the documentation.
```bash
# Push all tags
git push --tags
```
- I recommend not using or training others to use `git push --tags` as it can be very very difficult to get rid of bad tags when your co-workers are trained to push all tags, as people continue to push the old bad tags they have locally every time they want to push a new tag. Because of this, I will only every advise someone to use git push origin <tag_name> now.
### Don't change date when rebasing
#### Author date vs commit date
from the [Pro Git Book](https://git-scm.com/book/en/v2/Git-Basics-Viewing-the-Commit-History):
>- The author is the person who originally wrote the work,
>- whereas the committer is the person who last applied the work.
#### Preserving the author date
https://stackoverflow.com/a/2976598
```bash
git rebase --committer-date-is-author-date master
```
#### `--committer-date-is-author-date` vs `--ignore-date`
From [git am docs](https://git-scm.com/docs/git-am):
- `--committer-date-is-author-date` "_[...]allows the user to lie about the committer date by using the same value as the author date_" while
- `--ignore-date` "_[...]allows the user to lie about the author date by using the same value as the committer date_".
In this case, the one we want is `--committer-date-is-author-date`.
### [Get name of default branch](https://stackoverflow.com/questions/28666357/git-how-to-get-default-branch) (`master`, `main`)
(e.g. for usage in scripts)
```bash
git remote show upstream | grep "HEAD branch" | sed 's/.*: //'
```
### [Git pull and overwrite files](https://stackoverflow.com/a/8888015)
Not actually git pull, but it's a reasonable way to think about it
```bash
# Git fetch
git fetch <remote>
git reset --hard <remote>/<branch>
```
## Debugging
### `git commit --fixup <TAB>` completes with files instead of commits
- Also applies to use via a `gcf` alias
**Notes**
Had problems with this with the `brew` installation of `
[email protected]`.
Originally I thought the problem might have been with [[Oh My Zsh]] but extensive searches through the git history (and bisecting) produced no changes in behavior.
I thought I traced the problem to the `_git_commit ()` function in `/opt/homebrew/Cellar/git/2.44.0/share/zsh/site-functions/git-completion.bash`, but was not able to fix it satisfactorily. Multiple tweaks and iterations with an AI made some progress - I was able to substitute `__git_complete_refs` (or something like that) in the `--fixup` branch with a `git log ...` command which prints the commit hash and commit name. However, I could not get the tabbing behavior to work the way I needed, in particular, you can see the commit name while tabbing through, but only the commit hash is left behind.
Ended up just uninstalling the [[Homebrew]] `
[email protected]` entirely and going with the preinstalled macOS version of git. So as of 2024-11-06, the macOS `
[email protected]` appears to work correctly.
```bash
$ git --version
git version 2.39.3 (Apple Git-146)
```
The `git-completion.bash` file for the macOS version of `git` can be found at `/Applications/Xcode.app/Contents/Developer/usr/share/git-core/git-completion.bash`, in case further inspection is needed.
Let's hope that the issue in `
[email protected]` eventually gets fixed so I can upgrade.