-
Notifications
You must be signed in to change notification settings - Fork 0
/
git-mechanics.qmd
125 lines (105 loc) · 11.4 KB
/
git-mechanics.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# Git Mechanics
## An Overview of the Main Commands & Terms
There are many commands that you could learn in Git, but these are the basics, and will be sufficient for pretty much everything you'll need to do at the moment.
I've added a few extras that you will likely come across, so it's worth having at least a rudimentary understanding of what they mean, and where they might be useful.
As you get more advanced, you'll want to explore them in a little more detail.
### Core Commands
<div class="box">
- [**add**](#add)
- [**stage**](#stage)
- [**commit**](#commit)
- [**diff**](#diff)
- [**amend**](#amend)
- [**fetch**](#fetch)
- [**pull**](#pull)
- [**push**](#push)
- [**branch**](#branch)
- [**checkout**](#checkout)
- [**merge**](#merge)
- [**pull request**](#pull-request)
</div>
### Useful to Know About
<div class="box">
- [**revert**](#revert)
- [**reset**](#reset)
- [**rebase**](#rebase)
- [**rebase -i**](#rebase-i)
- [**HEAD**](#head)
- [**squash**](#squash)
- [**cherry pick**](#cherry-pick)
- [**reflog**](#reflog)
</div>
## Term Descriptions
### Core
- <span id="add">**add**</span>: this takes all of the changes you have made to a file/set of files, and **stages** them, so they are ready to be committed
- This is essential, as it allows you to work as you normally would, but save your changes in small chunks for more descriptive **commits** that are easier to understand and review.
- `git add .` stages all files that have been modified, but you can specify specific files by explicitly naming them.
- <span id="stage">**stage**</span>: *this is the process of preparing a change/set of changes to be **committed**.
- Think of it as putting a piece of code in a folder, ready to be saved.
- You can **stage** as many changes as you like, including lines of code (AKA **hunks**) rather than whole files, and then **commit** them all at once.
- If you have made changes to multiple files without writing any **commits**, you might not want to include all these changes in a single **commit** as the changes might be unrelated, or just too big to be useful so would be better served by splitting up into multiple distinct **commits**. **Staging** allows you to only prepare a subset of your changes for **committing**.
- <span id="commit">**commit**</span>: this standings for *committing* a change to your file in Git.
- Think of it as saving a document, but instead of saving the whole document as-is, Git saves just the changes since the last version. This makes it very efficient, especially when it comes to backing up your work.
::: {.callout-important}
- **commit** often. By making and saving small changes, your code versions becomes more readable in case you need to go back and find out exactly what and where it went wrong.
- Always write helpful messages - keep them succinct, but make sure they describe what the change you made was.
:::
- <span id="diff">**diff**</span>: this command shows you what has changed in a file since the last **commit**
- This is your "tracked changes" view!
- <span id="amend">**amend**</span>: this command add your changes to the most recent **commit**, rather than creating a new one.
- This is useful when you forget to include something in a **commit**, i.e., it is a small change that belongs in the most recent **commit** and is not a substantial piece of work, even if the two are related
- You never want to try to **amend** if your most recent **commit** has been **pushed** to the **remote**. You end up in a situation where collaborators might have already **pulled** your work so they are now out-of-sync with your rewritten git history, therefore git will not allow you to **push** these changes.
- In this situation, just create a new **commit**!
::: {.callout-note}
Technically, you actually **amend** the **commit** at the **HEAD**.
Given that the **HEAD** is the most recent **commit** by default, this is something you shouldn't need to worry about for now.
However, this distinction is important when it comes to [**interactive rebases**](#rebase-i).
You can see [this document](https://github.com/jesseduffield/lazygit/wiki/Interactive-Rebasing#step-2-1) for more information about **interactive rebases**, which highlights visually where the **HEAD** is in relation to the **commit** you are **amending**.
:::
- <span id="fetch">**fetch**</span>: checks the status of your **remote** and compares it to the version on your local machine, telling you if you are out of date i.e., need to **pull**
- <span id="pull">**pull**</span>: this command copies the version of the code from your remote to your local machine.
- Use this when you want to get the most up-to-date version of your code to work on (assuming your local version isn't the most up-to-date)
- <span id="push">**push**</span>: the opposite of **pull**. If your local version is the most up-to-date version, **push** your version to the remote.
- You should try to do this a few times a day, but certainly less frequently than you **commit** to allow yourself some time to correct any mistakes before they are cemented into the git history
- <span id="branch">**branch**</span>: a branch is a specific version of your code that has its own git history, separate from the code and history of other branches.
- This is useful for working on different features at the same time, as you can keep them separate until you are ready to merge them into the main code base.
- See [this section](how-git-works.qmd#branches) of the introduction for an overview, and the [branching section](branching-strategies.qmd) for more details about how you can use branches to your advantage.
- <span id="checkout">**checkout**</span>: changes the branch that you are working on
- <span id="merge">**merge**</span>: merges code changes from one branch into another i.e., keeps the git history separate for each branch, but at the merge point reconciles the differences
- Most of the time this will work without issues, but occasionally if the two branches have made changes to the same line of code, you may get a merge conflict where you need to tell git which version of the code it should keep in the final merged state.
- <span id="pull-request">**pull request**</span>: this is not a feature or command of git itself, but of GitHub (and other remote repositories). It is effectively a merge that takes place online to the **remote**, rather than to your local version
- This is useful as it allows for mechanisms like code checks before changes are merged into a branch, helping to minimize merge conflicts that can happen when multiple people change the same file sections during the same period of time between **pushes** to the **remote**.
### Useful Extras
- <span id="revert">**revert**</span>: creates a new **commit** that undoes the changes made during a specific **commit**
- This is a useful and safe way of rolling back work as it does not delete any git history.
- More applicable for public repositories that **reset**, as multiple collaborators rely on a shared git history, therefore it is critical this does not change unexpectedly.
- <span id="reset">**reset**</span>: this command set the current branch **HEAD** to whichever **commit** you are choosing to **reset** to i.e., moves the working state of the branch back
- You do not need to specify a particular **commit** - this will just **reset** to the previous **commit**
- There are 3 main types of **reset**:
- `reset --soft`: Will not reset any files that have been **staged** but not **committed**. All changes in previous **commits** will be uncommitted, but will still exist and saved as **staged** changes, ready for you to **commit** them again.
- `reset --mixed`: Will reset any files that have been **staged** but not **committed**. All changes in previous **commits** will be uncommitted, but will still exist. Unlike **reset --soft**, these changes are **unstaged** changes by default, so you will have to **add** (**stage**) them before you can **commit** them again. This is useful to **unstage** files you **staged** by accident, without deleting the code modifications you made.
- `reset --hard`: Unstages all files and changes all files back to the version specified e.g., `git reset --hard` (without a commit specified) deletes all the **uncommitted** code changes since the last **commit**. If you specify a **commit** e.g., `git reset --hard 1a23b456` you delete every change after `commit 1a23b456`
::: {.callout-tip}
I would recommend watching [this video](https://www.youtube.com/watch?v=s1idhUiCk38) to get a better understanding of how **reset** works
:::
- <span id="rebase">**rebase**</span>: instead of **merge**, where the histories of each branch are retained, **rebase** moves all the **commits** from one branch onto the tip of the the other branch
- When you are getting started, you rarely want to use **rebase** over **merge**
- <span id="rebase-i">**rebase -i**</span>
- There is a version of **rebase** called the interactive rebase that uses the command **rebase -i**
- The interactive rebase allows you to completely rewrite the git history, including splitting up a **commit** into multiple smaller ones.
- It is far beyond the scope of this workshop, and you should really think hard about whether it's necessary as it's easy to mess up your git history, but if you need to do this then you can find more information [here](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History)
- <span id="HEAD">**HEAD**</span>: this is a description of which **commit** git points to
- When you **checkout** a branch, the **HEAD** is set to the last **commit** in that branch, by default.
- However, you can choose to move the **HEAD** back down the branch's history, i.e., **checkout** a specific **commit**.
- This is called a **detached HEAD** state.
- This does not delete the **commits** that have happened since, but it does mean any changes you now make will diverge from the state of the code present at **that commit** and can not be accessed as they are not created within a branch.
- You should create a new **branch** if you intend to create new **commits**
- <span id="squash">**squash**</span>: this combines multiple **commits** into one
- You will rarely want/need to do this, particularly when starting out, but sometimes it can clean up the git history when performing a pull request that targeted a distinct new feature, and after a code review, doesn't need all the changes to be recorded in separate **commits**.
- Easiest to perform during a pull request on the GitHub interface.
- <span id="cherry-pick">**cherry pick**</span>: a command that allows selected **commits** to be appended to the current working **HEAD**.
- This can be incredibly useful when you have local **commits** that you would like to move to a different branch, or if you would like to split up a **commit** into smaller ones.
- <span id="reflog">**reflog**</span>: git records every command you make in the *reference log*, including **checking-out** branches, and the **git reflog** shows you this log
- Normally, this is not necessary to reference, but it can be useful if you end up in a position where you've **reset** a branch, and realize you didn't mean to do that.
- You can reference the **reflog** to show which commands you want to roll back, and **checkout** that **detached HEAD** state, before carrying on as normal
- `git checkout HEAD@{1}` would roll back one position (the end of the branch - the **attached HEAD** - sits at `HEAD@{0}`)