Skip to content

Commit e88da16

Browse files
committed
Initial commit
0 parents  commit e88da16

7 files changed

+3349
-0
lines changed

README.md

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
# Git
2+
3+
This guide assumes Git version 2.28.0.
4+
You can install it from [Homebrew](https://brew.sh/) with `brew install git`.
5+
The version of Git that comes with macOS is older, so if some of these commands don't work, check `git --version`.
6+
7+
## Core Concepts
8+
9+
### Commits
10+
11+
A commit is the core unit of change in Git.
12+
Each commit adds or removes lines in files.
13+
14+
<img src="structure-of-a-commit.png" width="639" />
15+
16+
Every commit has a hexadecimal hash (`36ee8abf2`...) that uniquely identifies it among all the other commits in the repository.
17+
`git log` shows the full 40-character hash for each commit.
18+
Since 40-character hashes are cumbersome, GitHub often shows just the first 6-9 characters of the hash (`36ee8ab`), just enough to identify it uniquely, and hides the rest.
19+
20+
Commits point to their parent commit.
21+
For example, if I make three commits in a repository, A &larr; B &larr; C, C would be the most recent commit, B its parent, and C its grandparent.
22+
`git log` shows the most recent commit and all of its parents.
23+
24+
Git can reconstruct the repository at any point in time by applying the sequence of added and removed lines in the commit chain.
25+
When we make changes by adding commits or merging pull requests, Git looks at those commits to know which lines to add and remove.
26+
27+
### `HEAD`
28+
29+
In Git, `HEAD` is a special alias for the most recent commit.
30+
It's whichever commit `git log` would show first.
31+
When we create a new commit, the previous `HEAD` commit is the new commit's parent, and then `HEAD` is updated to point to the new commit.
32+
33+
### Branches
34+
35+
Branches are special labels that point to commits.
36+
`master` is a regular branch whose label is "master".
37+
Sometimes repositories choose to call it `main` instead.
38+
Because we start work by branching from `master`, our branches look like a tree with `master` as the trunk.
39+
We can follow commits' parents in a chain and eventually end up back in the `master` trunk.
40+
41+
<img src="branches.png" width="620" />
42+
43+
We can create our own branches with `git branch new-branch-name`.
44+
This will create a new label pointing to the most recent commit.
45+
46+
We can switch between branches with `git switch other-branch`.
47+
48+
When we create a new commit, the current branch's label automatically updates from the previous commit to the new commit.
49+
50+
### Pushing and pulling
51+
52+
Our laptops, GitHub, and each Heroku environment all have their own copy of our repository.
53+
These repositories keep track of branches and commits separately.
54+
We use `git push` and `git pull` to copy commits and branch labels between repositories.
55+
56+
`git remote --verbose` lists all of the other repositories that Git knows about.
57+
GitHub is typically called `origin`, and `production` and `staging` might be names for Heroku's repositories.
58+
59+
When we push a new branch to GitHub, it pushes all of the commits we've made locally and then tells GitHub to create its own copy of the branch label pointing to the most recent commit.
60+
61+
When we push new commits to an existing branch in GitHub, Git sends GitHub all the new commits that are in our laptop's branch but not yet in GitHub's branch and updates GitHub's branch label to point to the newest commit.
62+
63+
Pulling works in reverse.
64+
When we pull a branch from GitHub, Git retrieves all the commits that are in GitHub but not yet on our laptop and updates our laptop's branch label to point to the newest commit.
65+
66+
## Working on code
67+
68+
The `master` branch should be the authoritative source for what exists in production.
69+
Changes should only reach `master` when the tests are passing and they're ready for production.
70+
71+
### Starting a feature or bug fix
72+
73+
We work on in-progress changes in "feature branches" off of `master`.
74+
We feature branch might implement a new feature, add a test, change some copy, or fix a bug.
75+
76+
```sh
77+
# Make sure we're starting our branch off of master.
78+
$ git switch master
79+
# And make sure we have the latest changes from master on GitHub.
80+
$ git pull
81+
# Create the feature branch from master. Name it something descriptive, like
82+
# `very-cool-feature` or `fix-issue-123`.
83+
$ git branch new-branch-name
84+
# Switch to the new branch.
85+
$ git switch new-branch-name
86+
```
87+
88+
At this point, we're working on the `new-branch-name` branch.
89+
For now, `HEAD`, `new-branch-name`, and `master` all point to the same most recent commit.
90+
Any new commits will be added to `new-branch-name` but not `master`.
91+
92+
### Committing changes
93+
94+
Once we've made a change, we use `git add` to prepare the added and removed lines to be turned into a commit.
95+
When we `git add` a file, it goes into a "staging" area, which is like a waiting room for the lines that will be in a commit.
96+
97+
```sh
98+
# Show what files have been changed since the last commit.
99+
$ git status
100+
# Add a file.
101+
$ git add hello-world.js
102+
# Show that hello-world.js has been added to the commit staging area.
103+
$ git status
104+
# Turn the staged files into a commit.
105+
$ git commit
106+
```
107+
108+
The `git commit` command will open a text editor where you can write a title and message for your commit.
109+
The title is a short description in a few words.
110+
The message contains a more detailed description of what changes we made and why.
111+
If we're adding a new feature, we describe the feature's design and how it's built.
112+
If we're fixing a bug, we describe what caused the bug and why this is the right way to fix it.
113+
114+
### Opening a pull request
115+
116+
Congrats!
117+
We've implemented a new feature or fixed a bug.
118+
All the steps in the change are nicely packaged as line changes in commits.
119+
It's time to open a pull request in GitHub.
120+
121+
```sh
122+
# Tell GitHub about our new commits and branch.
123+
$ git push --set-upstream origin my-new-branch
124+
```
125+
126+
`git push` copies all of the new commits to GitHub and creates a copy of our branch label.
127+
`origin` is Git's name for GitHub's copy of our repository.
128+
`--set-upstream` tells Git to remember that we pushed `my-new-branch` to GitHub, so in the future, just `git push` will automatically go to GitHub.
129+
Now go to GitHub and fill out your pull request.
130+
131+
#### Review feedback
132+
133+
We've received review feedback on the pull request and need to make changes.
134+
Make commits just like when we were working on the change initially.
135+
136+
```sh
137+
# Show the files we've updated in response to feedback.
138+
$ git status
139+
# Add them to the staging area for the next commit.
140+
$ git add hello-world.js
141+
# Commit our review fixes.
142+
$ git commit
143+
# Push the fixed commits to GitHub.
144+
$ git push
145+
```
146+
147+
Git will send our review feedback changes to GitHub, update GitHub's `my-new-branch` branch label to point to the new commits, and show them in the pull request.
148+
In the `push` step, since we used `--set-upstream` when we first pushed the branch to GitHub, we don't need to specify `origin my-new-branch` again because Git remembers those settings.
149+
It's the same as running `git push origin my-new-branch`, just with less typing.
150+
151+
#### Suggested changes
152+
153+
Sometimes reviewers leave small suggested changes in review comments.
154+
Committing these suggestions to the pull request is quick and easy without ever leaving GitHub.
155+
After you commit review suggestions from GitHub, be sure to update your local repository so it knows about the new commits.
156+
157+
```sh
158+
# Switch to the pull request's branch.
159+
$ git switch my-new-branch
160+
# Update the local branch with the suggested change commits from GitHub.
161+
$ git pull
162+
```
163+
164+
#### Rejected push
165+
166+
If we forget to update our local branch when there are new commits in GitHub's copy of the branch, Git will reject our next push to the pull request.
167+
**Never** run `git push` with `--force`.
168+
That would overwrite the suggested change commits from GitHub, losing work.
169+
Git rejected the push because there are commits in GitHub's copy of `my-new-branch` that are missing from our local copy of `my-new-branch`.
170+
We want to combine the commits from all versions of the branch.
171+
Run `git pull --rebase` to take the suggested change commits from GitHub and then replay our local commits after them.
172+
173+
<img src="rebase-suggested-change.png" width="798" />
174+
175+
```sh
176+
# Git rejects our push because our local branch is missing some commits.
177+
$ git push
178+
To https://github.com/drivecapital/git-workflow.git
179+
! [rejected] my-new-branch -> my-new-branch (non-fast-forward)
180+
error: failed to push some refs to 'https://github.com/drivecapital/git-workflow.git'
181+
hint: Updates were rejected because the tip of your current branch is behind
182+
hint: its remote counterpart. Integrate the remote changes (e.g.
183+
hint: 'git pull ...') before pushing again.
184+
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
185+
# Pull the newer commits from GitHub and replay our local commits after them.
186+
$ git pull --rebase
187+
# Now we can push the combined commits to GitHub.
188+
$ git push
189+
```
190+
191+
### Merge conflicts
192+
193+
Sometimes GitHub says we can't merge a pull request because we have merge conflicts.
194+
This happens when a teammate worked on the same lines in a different branch.
195+
Git doesn't know which version of the new lines to take - ours or our teammate's, so it asks us to decide.
196+
197+
First, we need to pull the latest changes from GitHub's `master` branch label to our laptop.
198+
199+
```sh
200+
# Switch to our local master branch.
201+
$ git switch master
202+
# Retrieve the new commits from GitHub.
203+
$ git pull
204+
```
205+
206+
_TODO: Finish this section._
207+
208+
## Git etiquette
209+
210+
_TODO: A commit should change one thing, and pull requests should only include related commits. How to make things easier for reviewers._
211+
212+
## Advanced usage
213+
214+
### Reverting a pull request
215+
216+
If we just merged and deployed a pull request that broke everything and we can't fix it in a hurry, the revert button is the nuclear option.
217+
Clicking the "revert" button on the offending pull request will reverse all of its changes, removing the lines it added and adding back the lines it removed.
218+
If we do a good job submitting pull requests that make logically grouped changes and splitting unrelated changes into their own pull requests, we should be able to revert just the pull request that caused the breakage.
219+
We shouldn't need to use the `git revert` command locally.
220+
221+
### Committing only part of a file
222+
223+
Each commit should do one thing.
224+
If we've fixed two unrelated bugs, we'd like to create two commits, one to fix each bug.
225+
We don't have to `git add` an entire file at once. Instead, we can add only part of a file.
226+
227+
```sh
228+
# Start an interactive session to stage lines for the commit a chunk at a time.
229+
$ git add --patch
230+
```
231+
232+
`git add --patch` will go through the changes one chunk of lines at a time.
233+
Typing `?` at the prompt will show help instructions.
234+
`y` will stage this chunk of lines for inclusion in the next commit.
235+
`n` will leave this chunk of lines unstaged for a later commit.
236+
If the chunk of lines is too big, split it into even smaller chunks with `s`, then stage or skip the sub-chunks with `y` or `n`.
237+
238+
### Un-staging changes
239+
240+
If you've added a file to the commit staging area and don't want it to be part of the commit, you can remove it from the staging area before committing.
241+
242+
```sh
243+
# Show the files in the staging area for the next commit. Whoops, hello-world.js
244+
# isn't supposed to be part of this commit! Let's leave it for a later commit.
245+
$ git status
246+
# Remove hello-world.js from the staging area.
247+
$ git unstage
248+
# Show that hello-world.js has been removed from the commit staging area. A new
249+
# commit now would not include the added and removed lines in hello-world.js.
250+
$ git status
251+
```
252+
253+
### Interactive rebase
254+
255+
_TODO_
256+
257+
## Configuration
258+
259+
When pulling from GitHub, only allow "fast forwards".
260+
When "fast forwarding", if your local branch contains commits A, B, C, and GitHub has A, B, C, D, E, pulling will add D and E to your local branch.
261+
262+
```sh
263+
$ git config --global pull.ff only
264+
```
265+
266+
Enable branch protection in your GitHub repository's settings:
267+
268+
- `master`
269+
- Don't allow force pushes
270+
- This only applies to `master`.
271+
- We can still `--force-with-lease` to feature branches in advanced uses.
272+
- Don't allow deletions
273+
- Include administrators
274+
- Prevents even organization owners from accidentally force pushing to `master`.
275+
- If 💩 hits the fan and you really need to, you can temporarily un-check this.
276+
- Optionally, require pull request reviews before merging
277+
- Changes must go through code review.
278+
- Prevents accidentally pushing directly to `master`.
279+
- Optionally, require status checks to pass before merging
280+
- If your automated tests aren't flaky.
281+
- Optionally, require linear history
282+
- Forces people to resolve conflicts via rebasing their feature branch on `master` instead of merge commits.

0 commit comments

Comments
 (0)