# Git

## What is Git?

* Git is a popular open-source version control tool commonly used for code but can be used with all types of files
* More information about Git can be found on its [website](https://git-scm.com/)

## Install

* Git is typically installed by default on Linux distributions and MacOS however it will need to be installed on Windows
  * [Git Download](https://git-scm.com/downloads)
  * Windows: `winget install git.git`
  * Linux: `sudo apt install git`
  * MacOS: `brew install git`

## Pushing Local Code to a Remote Repository

* With an empty remote repository, we can add local code to it

{% code overflow="wrap" fullWidth="false" %}

```bash
# set the origin to the remote repo
git remote add origin <repoUrl>

# push local code to the remote main branch
git push -u origin main
```

{% endcode %}

## Cloning Repositories Locally

* We can download a repository locally if we have permission

{% code overflow="wrap" %}

```bash
# via HTTPS
git clone <URL> 

# via SSH
git clone <sshLink>
```

{% endcode %}

## Merging Code

* When you want to merge your changes to a repo
* GitLab - Merge Request (MR)&#x20;
* GitHub - Pull Request (PR)

## Merge Conflicts

* This can occur during a merge when your file is different than an existing file and git is unable to determine which changes should be committed.
* The error may look like this after running `git push origin <branchName>`

{% code overflow="wrap" %}

```bash
! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'http://gitlab.com/tyler/myproject.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
```

{% endcode %}

* To fix, run `git pull origin <branchName>` (the conflicting branch) and identify the problem file(s) in the output.

```bash
[snip]
CONFLICT (add/add): Merge conflict in file.txt
Auto-merging file.txt
Automatic merge failed; fix conflicts and then commit the result.
```

* Open the file (GUI, IDE, etc.) and select the data to keep removing all the git added stuff and leaving only the data you want.

```bash
# file.txt

<<<<<<< HEAD # your changes
My changes are here!
=======
The data in the branch!
>>>>>>> 2lk3lm23lrl2k3ml2m3l2n3ln2l3lsdkglsadg # the commit hash on the remote branch
```

```bash
# file.txt

My changes are here!
```

## Fork

* Someone with READ access can download your repo/code, make changes, and then open a Merge Request for you to approve

## Rebasing

* When the branch you're working on is behind commits from others&#x20;

### Merging

* Running this command from your branch brings changes from others into your branch and keeps the git commit history intact

```bash
git merge main
```

### Rebasing

* Running this command moves your branch on top of the updated branch, changing the git commit hashes from others.&#x20;
* Not a big deal but your coworkers may not like this due to the commit history changing

```bash
git rebase main
```

### Squashing Commits

* When you have multiple commits and want to squash them into one
* The final commit will be `Addng new feature`

```bash
# view commit history
git log

# squash the last 3 commits into one
git rebase -i HEAD~3

# replace `pick` with `squash`
pick 3h3ih Adding new feature
squash 4j69j Fixing typo
squash 6k49g Adding change
```

* If you've already deployed a commit to a remote branch, but now need to revert it or change it, here's how you can squash them,

{% code overflow="wrap" %}

```bash
# view commit history
git log
commit ljsdflhsd3o2h8hd
Author: <author>
Date: <date>

    <latest commit message>
    
commit 9sdfh98sh9f839hh3
Author: <author>
Date: <date>

    <previous commit message>
```

{% endcode %}

* Let's say you want to squash the new changes into the old commit

```
# select last two commits
git rebase -i HEAD~2

# merge remote branch into yours
git pull 

# push latest commit to remote branch
git push
```

## Cherry Picking

* When you want to incorporate a particular commit from another branch into your branch

```bash
git cherry-pick <commitHash>
```

## Reverting Commits

* You've committed some changes and now want to undo them

### Keeping Commit History&#x20;

* Removing the commit shows the file(s) as deleted in the commit history

```bash
git revert <commitHash>
```

### Undoing Commit

* This undoes your commit
* Use `--soft` to undo the commit but keep your modified file(s)
* Use `--hard` to undo the commit and delete your modified file(s)

```bash
# remove last commit ~1
git reset --soft HEAD~1
git status

    On branch featureA
    Changes to be committed:
        added:    file.txt
    

# remove last commit ~1
git reset --hard HEAD~1
git status

    On branch featureA
    Nothing to commit
```

## Stashing

* Storing your code when you don't want to commit it yet but need to hop to another branch and don't want your modified code to come with you

```bash
# stash code
git stash

# list code kept in stash
git stash list

    stash@{0}: WIP on main: added new feature X (1 hour ago)
    stash@{1}: Bugfix for issue#123 (2 days ago)
    stash@{2}: Update README.md (yesterday)


# show contents of stash
git stash show stash@{0}

    diff --git a/new_feature.py b/new_feature.py
    new file
    +++ b/new_feature.py
    

# remove stashed item
git stash pop stash@{1}
```

## Reflog

* Shows the state of the repository and enables you to undo mistakes e.g., you deleted a file

```bash
# show changes
git reflog

    34o4on43mf0sd  HEAD@{0}: checkout: moving from main to feature (HEAD@{0})
    has34o4omf0sd  HEAD@{1}: commit: added feature X (2 hours ago)
    64osafdamf0sd  HEAD@{2}: commit: initial commit (yesterday)
    78o4sdsdh330s  ORIG_HEAD@{0}: commit: initial commit (yesterday)
    

# undo a change
git reset --hard <commitHash>
```

## Useful Commands

### General

```bash
# get help
git --help
man git

# download/clone repo
git clone <repositoryLink>

# view changes made since last commit or initial download 
git status

# queue files for commit
git add <fileName>
git add *

# commit files
git commit -m "description of change"

# push files to branch
git push
git push -u origin <branchName>
git push --set-upstream origin <branchName> 

# view git log
git log
git log -p <commitHash>

# checkout a commit
git checkout <commitHash>
```

### Branches

```bash
# view all local branches
git branch

# view all remote branches
git branch -r

# view all local and remote branches
git branch -a

# create new branch and switch to it
git checkout -b <newBranchName>

# switch branch
git branch <branchName>

# rename branch
git branch -m <oldBranchName> <newBranchName>
```

## Modify Terminal to show current git branch

* Update your shell's configuration file e.g., `~/.zshrc` or `~/.bashrc`
* Save and then reload your shell for the changes to take effect `source ~/.zshrc`

```sh
parse_git_branch() {
    git branch 2> /dev/null | sed -n -e 's/^\* \(.*\)/[\1]/p'
}
# color #s found here: https://misc.flogisoft.com/bash/tip_colors_and_formatting
COLOR_DEF='%f'
COLOR_USR='%F{243}'
COLOR_DIR='%F{197}'
COLOR_GIT='%F{39}'
NEWLINE=$'\n'
setopt PROMPT_SUBST
export PROMPT='${COLOR_USR}%n@%M ${COLOR_DIR}%d ${COLOR_GIT}$(parse_git_branch)${COLOR_DEF}${NEWLINE}%% '
```
