因?yàn)樵奶L超出字?jǐn)?shù),Lesson 3 就放在另一篇文章里
How to Use Git and GitHub
標(biāo)簽(空格分隔): Udacity
Course Syllabus
Lesson 1: Navigating a Commit History
Lesson 2: Creating and Modifying a Repository
Lesson 3: Using GitHub to Collaborate
[TOC]
Lesson 1: Navigating a Commit History
1.1 Finding Diffs Between Larger Files

in windows

in Linux and Mac

1.2 Reflections

1.3 Properties of a VCS for Code
VCS:Version Control System

1.4 Manual Commits in Git
這個Commit的概念在git中很重要

1.5 Creating a Concept Map

1.6 Using Git to View History
使用git log
來查看history。每一個commit都有一個ID, Author, a data and a message assicioated with it.

用兩個commit的ID來代替文件名,通過git diff commit_1_ID commit_2_ID
來查看兩個commit之間的差別。這個效果和之前在shell里看到的效果是一樣的,而且git中還有高亮。

1.7 Concept Map: diff

1.8 One Commit per Change Instructions
1.8.1 How Often to Commit
A good rule of thumb is to make one commit per logical change. For example, if you fixed a typo, then fixed a bug in a separate part of the file, you should use one commit for each change since they are logically separate. If you do this, each commit will have one purpose that can be easily understood. Git allows you to write a short message explaining what was changed in each commit, and that message will be more useful if each commit has a single logical change.
1.8.2 Commit Size Quiz

1.8.3 One Commit per Logical Change Solution
You commit all the changes required to add a new feature, which you’ve been working on for a week. You haven’t committed since you started working on it.
This commit seems too big. It's easier to understand what each commit does if each only does one thing and is fairly small. Going a week without committing is not the best idea.
You found three typos in your README. You fix and commit the first.
This commit seems too small. It would be better to fix all three typos, then commit. That way, your history won't get too cluttered with typo fixes. Plus, you don’t need to worry about introducing bugs to a README, so bundling changes together is more likely to be a good idea.
You commit all the changes required to add a new feature, which you’ve been working on for an hour.
This is probably a good size for a commit. All the work is on a single feature, so the commit will have a clear logical purpose. After an hour, the diff will probably have a fair amount of content in it, but not too much to understand.
On the other hand, sometimes after working for an hour you’ll have run into more than one natural committing point, in which case you would want to break the feature up into smaller commits. Because of this, “too big” could also be a reasonable answer here.
You fix two small bugs in different functions and commit them both at once.
This commit is probably too big. It would have been better to commit after the first bug fix, since the two bug fixes aren't related to each other.
Judgment Call
Choosing when to commit is a judgment call, and it's not always cut-and-dried. When choosing whether to commit, just keep in mind that each commit should have one clear, logical purpose, and you should never do too much work without committing.
1.9 Tracking Across Multiple Files

1.9.1 Git Commits Across Multiple Files

if you have a project to work on, you'll often have multiple files that you want to tract together. Git calls such collection of files a repository.
when you save a version in git, in other words, when you make a commit, you will save a version of every file in your repository.

在shell中實(shí)際操作一下.下面的命令可以檢查which files have changed in each commit.
git log --stat

game.js | 5 +++--
表示有5處change,3處addition,2處deletion。
圖中高亮的部分就是一個commit有三個文件同時(shí)changed.
通過git diff ID1 ID2
能看到兩個commit之間三個文件的變化情況。

1.10 Cloning and Exploring The Repo

用git clone
得到本地的repo后,就不用再聯(lián)網(wǎng)了

- Cloning a Repository
To clone a repository, rungit clone
followed by a space and the repository URL. - Exiting
git log
To stop viewinggit log
output, pressq
(which stands for quit). - Getting Colored Output
To get colored diff output, rungit config --global color.ui auto
- Using git log and git diff
As a reminder, runninggit log
will show a list of the recent commits with information about them, including commit IDs. Runninggit diff
followed by two commit IDs will compare the two versions of the code in those commits. - Entering commit IDs
If it is easier, you may enter the first four or more characters of the commit ID rather than pasting the entire ID.
1.11 Concept Map: repository, clone, log


1.12 Git Errors and Warnings
Git Errors and Warnings Solution
Should not be doing an octopus
Octopus is a strategy Git uses to combine many different versions of code together. This message can appear if you try to use this strategy in an inappropriate situation.
You are in 'detached HEAD' state
HEAD is what Git calls the commit you are currently on. You can “detach” the HEAD by switching to a previous commit, which we’ll see in the next video. Despite what it sounds like, it’s actually not a bad thing to detach the HEAD. Git just warns you so that you’ll realize you’re doing it.
Panic! (the 'impossible' happened)
This is a real error message, but it’s not output by Git. Instead it’s output by GHC, the compiler for a programming language called Haskell. It’s reserved for particularly surprising errors!
1.13 Checking Out Old Versions of Code
Most Recent Commit
The commit ID of the most recent commit is 3884eab839af1e82c44267484cf2945a766081f3
. You can use this commit ID to return to the latest commit after checking out an older commit.
Format of git checkout
The command Caroline types to checkout the "Revert controls" commit is git checkout b0678b161fcf74467ed3a63110557e3d6229cfa6
.
Entering commit IDs
If it is easier, you may enter the first four or more characters of the commit ID rather than pasting the entire ID.
git checkout講解:
we can also temporarily change our files back to how they were at the time of any commit. This is called a git checkout, and it's sort of like restoring a previous version. In git, checking out a commit means resetting all of your files to how they were at the time that commit was made.
為什么用git checkout:
One reason might be, if a bug was introduced, but you're not sure which commit introduced it. You can test wether a commit has the bug by checking out that commit and running the code.
示意圖

打開/home/xu/Udacity/version-control/asteroids/index.html,在網(wǎng)頁里可以進(jìn)行游戲,但是發(fā)現(xiàn)飛船的子彈是無限連續(xù)射出的,一定是有了bug。所以我們通過git checkout
回到寫有Revert controls
的那個commit ID。

回到那個ID后,發(fā)現(xiàn)有detached HEAD state
的信息。
I get this strange warning we mentioned before. You are in detached head state. Like we mentioned, head is what git calls the commit that you're currently working on, and you've detached it here by checking out an older commit.

再次打開index.html
,發(fā)現(xiàn)子彈是一個一個發(fā)射,雖然正常了,但是飛船顏色也沒用了。用git log
查看,發(fā)現(xiàn)之前的所有ID都沒有了,想回到之前的commit只要直到ID就可以了,問題是那個ID,已經(jīng)沒了?,F(xiàn)在我們直接用提前記好的ID,之后的可能有辦法解決這個問題。
我們要找到子彈的bug,how?
I want to check out each of these commits one at a time until I find the one with the bug. 然后發(fā)現(xiàn)是25ede這個ID下有了bug.If we want to know exactly how the bug got introduced, we can use git diff to compare this commits and the previous one.
1.14 Git Workspace
這一節(jié)就是設(shè)置git的一些高亮,比如修改文件后,會有*
號提示等等一些便于使用git的設(shè)置。不過用zsh+oh my zsh,使用git 插件后(選好主題),這些配置都自動設(shè)置好了,所以沒必要自己再去改配置。
Problem Set 1
1 Old File Plus Diff Quiz
左側(cè)是原始文件,中間是原始文件和另一個文件的比較結(jié)果,根據(jù)結(jié)果,重建出另一個文件。

2 Tracking Versions Using Git Quiz

Using git diff
to compare the two versions would show the same changes as diff -u
did in the previous exercise.
This is true.diff -u
and git diff
show very similar outputs. Even if the exact format was slightly different, the actual changes indicated would be the same.
3 Git Command Review Quiz
Compare two commits, printing each line that is present in one commit but not the other.
git diff
will do this. It takes two arguments - the two commit ids to compare.
Make a copy of an entire Git repository, including the history, onto your own computer.
git clone
will do this. It takes one argument - the url of the repository to copy.
Temporarily reset all files in a directory to their state at the time of a specific commit.
git checkout
will do this. It takes one argument - the commit ID to restore.
Show the commits made in this repository, starting with the most recent.
git log
will do this. It doesn't take any arguments.
4 Behavior of git clone Quiz

If someone else gives you the location of their directory or repository, you can copy or clone it to your own computer.
This is true for both copying a directory and cloning a repository.
As you saw in the previous lesson, if you have a URL to a repository, you can copy it to your computer using git clone
.
For copying a directory, you weren't expected to know this, but it is possible to copy a directory from one computer to another using the command scp
, which stands for "secure copy". The name was chosen because the scp
command lets you securely copy a directory from one computer to another.
The history of changes to the directory or repository is copied.
This is true for cloning a repository, but not for copying a directory. The main reason to use git clone
rather than copying the directory is because git clone
will also copy the commit history of the repository. However, copying can be done on any directory, whereas git clone
only works on a Git repository.
If you make changes to the copied directory or cloned repository, the original will not change.
This is true for both copying a directory and cloning a repository. In both cases, you're making a copy that you can alter without changing the original.
The state of every file in the directory or repository is copied.
This is true for both copying a directory and cloning a repository. In both cases, all the files are copied.
4 Behavior of git log Quiz

git log
lists the most recent commit first, as you can verify by checking the commit dates. The middle commit probably contains the code for the mute button, since the commit message indicates that the mute button was added in that commit. The top commit also probably contain the mute button, since that commit is more recent and nothing suggests the mute button has been removed. The bottom commit probably does not contain the mute button, since that commit was created before the commit that added the mute button.
5 Behavior of git diff Quiz
git diff
是有順序的.git diff A B
,其實(shí)A是old file, B是new file, 這樣才會顯示B比A多了什么,少了什么。 順序很重要。

The middle commit, 06d72e
, is the first commit with the mute button, so comparing that commit and the previous commit, 3d4d45
, would show the changes that add the mute button.
In order for the changes adding the mute button to be shown as additions, the commit with the mute button needs to be the second argument to git diff
. That is because git diff
considers the first argument as the "original", and the second argument as the "new" version, so additions are lines present in the second argument but not the first.
Thus, the last command listed, git diff 3d4d45 06d72e
, is correct, and would show the mute button lines as additions. Reversing the arguments and running git diff 06d72e 3d4d45
would instead show the mute button lines as deletions.
6 Behavior of git checkout Quiz

Checking out an earlier commit will change the state of at least one file.
This is sometimes true. Git doesn't allow you to save a new commit if no files have been updated, so you might think this is always true. However, it's possible to do the following:
- Save a commit (call this commit 1).
- Update some files and save another commit (call this commit 2).
- Change all the files back to their state during commit 1, then save again (call this commit 3).
就是說當(dāng)commit 2引入了bug后,我們checkout到commit 1,在修正bug后提交,這個時(shí)候創(chuàng)建的是commit 3。
This sometimes happens if commit 2 contained a bug, and it's important to fix the bug quickly. The easiest thing to do might be to remove all the changes introduced by commit 2 to fix the bug, then figure out how to safely reintroduce the changes later.
At this point, commit 3 is the latest commit, so if you checkout commit 1, none of the files will be changed.
Checking out an earlier commit will change the state of more than one file.
Checking out an earlier commit will change the state of every file in the repository.
Both of these are sometimes true. Since each commit tracks the state of all files in the repository, it is possible that checking out an earlier commit will change the state of multiple files, or even all the files in the repository. However, it is possible to save a new commit after changing only one file, so it is possible only one file will change.
After checking out a commit, the state of all the files in the repository will be from the same point in time.
This is always true. A commit saves a snapshot of all files in the repository at the time the commit was made, so checking out an earlier commit will result in all the files being reverted to their state at the time the commit was made. That is, the files will be in a consistent state.
7 Cloning a New Repository
clone一個新的游戲 Repository,叫Pappu Pakia.用這個來做練習(xí),找bug之類的。
7.1 Identifying a Bug
- Buggy behavior
If you started playing Pappu Pakia, you should have noticed some pretty strange behavior! The game seems empty of any obstacles, so it's pretty boring. Also, the bird (called a "pappu"), seems to flicker in various locations across the screen.
- Solution
The commit that introduced this bug has the ID 547f4171a82ec6429d002c1acef357aec41d3f17. One way to find this out would have been to run git log, which should have shown that the most recent 4 commits, and their commit ids, were:
commit fa4c6bade4970c282b3870ad16f1bde8164663a9
changing flattr link
commit 708bcce690e5faa5739bd471507c102ea16b77f7
pressing down arrow wont cause scroll down anymore
commit 547f4171a82ec6429d002c1acef357aec41d3f17
refactoring collision detection
commit 71d52709ddc4066e7a79a1d0a412e43429a0cdeb
removing old readme
(This output has been shortened to be easier to read.)
Then you could use git checkout to examine old commits and see which ones have the bug. You already know the most recent commit, "changing flattr link" has the bug, so you could run git checkout 708bcce690e5faa5739bd471507c102ea16b77f7
to test the second-most-recent commit . You should find that this commit also has the bug. Next, you'll find the commit "refactoring collision detection" also has the bug, but the commit "removing old readme" does not. That means the commit "refactoring collision detection" with commit ID 547f4171a82ec6429d002c1acef357aec41d3f17
, is the one that introduced the bug.
7.2 Fixing the Bug
To find the lines introduced by the buggy commit, you can use git diff
. You'll need the ID of the buggy commit, which you just found to be 547f4171a82ec6429d002c1acef357aec41d3f17
. Then you'll need the ID of the previous commit, which will be the commit below it in git log
. (That's because git log lists the most recent commit first.) That turns out to be 71d52709ddc4066e7a79a1d0a412e43429a0cdeb
.
Thus, by running git diff 71d52709ddc4066e7a79a1d0a412e43429a0cdeb 547f4171a82ec6429d002c1acef357aec41d3f17
, you can find out that the lines changed by the buggy commit were:
- return !(
- bounds1.end_x < bounds2.start_x ||
- bounds2.end_x < bounds1.start_x ||
- bounds1.end_y < bounds2.start_y ||
- bounds2.end_y < bounds1.start_y
- );
-
+ if (bounds1.end_x < bounds2.start_x) {
+ return true;
+ }
+ if (bounds2.end_x < bounds1.start_x) {
+ return true;
+ }
+ if (bounds1.end_y < bounds2.start_y) {
+ return true;
+ }
+ if (bounds2.end_y < bounds1.start_y) {
+ return true;
+ }
+ return false;
This change represents an "or" expression being separated out into several "if" statements. The number of functions did not change, and no variables were renamed.
- File changed
Near the top of the git diff
output, you can see the lines
--- a/js/utils.js
+++ b/js/utils.js
This indicates that the file changed was js/utils.js
, that is, the file utils.js
within the js
directory.
What caused the bug
Based on the change that was made, a reasonable guess is that the bug is some sort of logic error - maybe the new version does not return true and false at the correct times.
It turns out that this is correct. The new code has true
and false
reversed! Even if you weren't sure exactly why the bug was there, congratulations! You tracked down exactly where the bug was introduced, and knew which lines introduced it, without knowing the code base. All you had to know was how to use Git.
7.3 Identifying a Second Bug
** There is a second bug**
Now you should have a version of the code that works much better - your pappu is not flickering across the screen, and there are plenty of obstacles to avoid. However, there is another, harder to see, bug in the code.
During the game, a cluster of berries appears reasonably often. When the pappu hits those berries, it should split into three pappu clones, but instead, nothing seems to happen.
Finding the bug
This time, instead of checking out old versions of the code, just run git log and look at the 10 most recent commits. Based only on the commit messages, which commit do you think is most likely to have introduced this bug? You can't be sure just by reading the messages, but pick the one you think is most likely.
** Identifying a Second Bug Solution**
One reasonable guess is that the commit with message "speeding clones up", that is, commit 003c8c197cd3b1e5b28b58f53ee14d7ebaa9bb3a
, is likely to be the one causing the bug. The bug is related to clones, and this commit changed the behavior of clones, so it seems plausible that this commit caused the bug.
Of course, the most likely-looking commit won't always be the culprit, so you'll always have to take a closer look at the suspicious commit to see if it actually caused the bug. In this case, the commit "speeding up clones" did in fact cause the bug.
Using this strategy of examining the most likely looking commits doesn't always work, but it often does, and it can save a lot of debugging time. This is one of the reasons it's so useful to make one commit per logical change and give each commit a good message - to make it possible to take shortcuts like this!
7.4 Fixing the Second Bug
Changes introduced
As before, you can use git diff
to find the lines introduced by the buggy commit. Again, you'll need the ID of the buggy commit, which is 003c8c197cd3b1e5b28b58f53ee14d7ebaa9bb3a
, and the ID of the previous commit, which is 746f762e38b5bbb7d0b837464ef6ec3f8ee5bf91
.
Thus, by running git diff 746f762e38b5bbb7d0b837464ef6ec3f8ee5bf91 003c8c197cd3b1e5b28b58f53ee14d7ebaa9bb3a
, you can find out that the change made by the buggy commit was:
- clone.x += utils.randomNumber(5, 10);
- clone.y += utils.randomNumber(-20, 20);
+ clone.x += utils.randomNumber(500, 1000);
+ clone.y += utils.randomNumber(-2000, 2000);
That is, the x
and y
coordinate of each clone is changed by a larger random amount. This will have the effect of making the clones move more quickly, or speed up, since their positions change more quickly.
File changed
Near the top of the git diff
output, you can see the lines:
--- a/js/pappu.js
+++ b/js/pappu.js
This indicates that the file changed was js/pappu.js
, that is, the file pappu.js
in the directory js
.
What caused the bug
Based on the change that was made, one possible bug is that the clones move too quickly - so quickly they have left the screen before you see them. This turns out to be correct. If you change the code to have numbers bigger than the original numbers, but smaller than the new numbers, the clones will move more quickly, but still be visible. Some lines of code that work well are:
clone.x += utils.randomNumber(20, 40);
clone.y += utils.randomNumber(-30, 30);
Again, even if you weren't sure exactly why the bug was there, congratulations! You tracked down which lines introduced a bug without knowing the code base, just by using Git.
Lesson 2: Creating and Modifying a Repository
2.1 What Makes a Repository a Repository?
通過git clone
得到metadata后,有些hidden directory是看不到的。比如.git
.必須用ls -a
才能看到隱藏文件.而這個隱藏的.git
is the thing that Makes a Repository a Repository

2.2 Initializing a Repository
通過git init
來初始化。比如在recipes
directory下初始化,那么這個repository的名字就是recipes
。這個文件夾下的所有文件都會包括在repository里。

Git repositories and directories
Each Git repository is tied to a specific directory - the directory where you ran git init
. Only files from that directory (and subdirectories inside that directory) will be contained in that repository, and you can have different repositories in different directories.
Note: it's often the case that a Git repository in some directory will only contain, or track, some of the files in that directory, rather than all of them. You'll see how this works later this lesson.
QUIZ
git init
不會有commit ID,這個必須自己手動,而且要寫commit message.

2.2.1 Examining the New Repository
用git log
返回錯誤,因?yàn)?code>git init之后,沒有commit.但我們可以用git status
查看現(xiàn)在的repository 狀態(tài)。status shows which files have changed since the last commit.

2.3 Staging Area
利用staging area做一個緩沖區(qū),可以一下子commit多個files。通過git add
把working directory 里的files add to the staging area.



2.4 Concept Map: init, add, staging area

2.5 Writing Good Commit Messages
How to write a commit message
You're about to make your first commit to your reflections repository. When you do this, you'll need to write a commit message describing your changes. If you followed the instructions in the "Setting Up Your Workspace" video for your platform near the end of Lesson 1, the editor you chose will appear as soon as you run git commit
and allow you to write a commit message. If you get an error message, you should try revisiting the instructions in Lesson 1 and make sure your text editor is set up properly.
You can also specify a commit message via the command line by running git commit -m "Commit message"
instead of just git commit
. It's still a good idea to get an editor set up, since this will make it easier to write long commit messages that fully describe the change.
簡單一句話:不要用git commit
!要用這個:git commit -m "Commit message"
Commit message style
While commit message style varies from person to person, this style guide describes some common best practices when writing commit messages.
這個guide寫的很好,到了自己寫message的時(shí)候,一定要好好參考。
全部commit后,用git status
檢查,顯示nothing to commit, working directory clean。
2.6* git diff Revisited(重點(diǎn))
之前的git diff都是比較commit ID之間的,但是有時(shí)候我們也想比較working directory, staging area, and Repository之間的變化。比如剛剛take a break,回來的時(shí)候忘記modify了哪些部分。
先回顧一下之前用git diff找asteroids里bug的流程。(step 1 and step 2)
- 打開directory asteroids 的時(shí)候,發(fā)現(xiàn)當(dāng)前的ID是之前的一個ID,不是最新的
3884eab839
。而且game.js
也被modify了。發(fā)現(xiàn)必須得commit or discard the modification ofgame.js
,才能checkout
回到3884eab839
。于是用git checkout game.js
discard the modification, and thengit checkout 3884eab839
回到初始狀態(tài)?,F(xiàn)在這個狀態(tài)是最開始clone完的狀態(tài),有bug。 - 我們已經(jīng)知道了有bug的ID是
25ede836
with the message "a couple missing ends with the ipad verison", the previous one isdf035382
。用git diff df035382 25ede836
找到不同的地方。git39.png-118.4kBgame.js
,加上刪去的那一行。用git status
檢查,看到modified: game.js
. -
st index.html
打開后隨便找一行collapse the line onto the previous one. - 好了。我們現(xiàn)在有兩個文件處于modified。一個是
games.js
,這個文件之前有bug,子彈可以連續(xù)射出。通過比較bug的ID和previous one, 我們發(fā)現(xiàn)the bug one 少了一行,我們加上。再把index里面的隨便一行更改一下位置。每個modified file都有一個*
標(biāo)記,表示雖然更改了,但是沒有commit。(我用的注意是最右側(cè)有一個雷電的標(biāo)識表示有文件modify了)。
現(xiàn)在來看一下working directory, staging area, and Repository的狀態(tài)。
- The repository contains several commits. And each commit contains several files. 比如現(xiàn)在圖中的the most recent commit contain the
game.js
andindex.html
,這些是ID3884eab839
下的文件,也就是說有bug的文件。修改的文件還在working directory. - The staging area is a copy of the most recent commit until I add changes to it. So it has those same files.
- The working directory also has the same files in it, but I've made some updates to
game.js
andindex.html
,which I'll represet using these stars.

We know that we can use git diff
to compare two commits by entering their commit ID's. 但是staging area and working directory are not commits, so they don't have ID's.
- 那么我們該怎么比較staging area and working directory里的modification呢?
Solution: use git diff
to compare working directory and staging area, git diff
不加arguments. This will show any changes you've made that you haven't added to the staging area yet.

(注意兩個diff的用法)
試驗(yàn)一下,現(xiàn)在有game.js
and index.html
處于modification狀態(tài),run git diff
,結(jié)果會把staging area 里的文件當(dāng)做old file, wroking directory里的文件當(dāng)做new file:

- add
game.js
to the staging area and rungit diff
again
This time we only see the changes to index.html
,因?yàn)楝F(xiàn)在working directory and staging area里的game.js
是一樣的了。(since the game.js
is the same in the staing area and workign directory).Add a star to game*
in the staging area.
現(xiàn)在我們可以用git diff --staged
查看staging area and Repository之間的changes。因?yàn)楝F(xiàn)在只有game.js
不同,所以只會顯示the changes of games.js

run git diff --staged
:

-
git commit -m "Add delay back to bullets"
, thengit diff
git commit 之后,創(chuàng)建了新的commit ID 943a54d
,此時(shí)只有index.html
處于modefied狀態(tài),the game.js
is all same in three stage.

- 但如果我不想要
index.html
里的changes怎么辦?
use git reset --hard
, which discards any changes in either the working directory or the staging area. 但是用的時(shí)候一定小心。雖然在git里大部分的操作都是reversible,你可以隨時(shí)resotre previous commits,但是?。。ut you've never committed the changes in your working directory or staging area. So if you run this command, you can't get those changes back.

QUIZ
注意第二行,git diff --staged
比較的是staing area and commit1

2.7 Commit the Bug Fix
Leave 'detached HEAD' state
Right now, your HEAD should still be 'detached' from Lesson 1 when you checked out an old commit. To fix that, run the command git checkout master
. You'll learn more about what this command does later this lesson.
Fix the delay bug
Now, if you were following along with Caroline, you may have already fixed the bug in the Asteroids repository. If not, go ahead and make the change and add it to the staging area now.
In game.js
find the statement if (this.delayBeforeBullet <= 0) {
(should be on line 423). On the next line, insert this.delayBeforeBullet = 10;
Instructor Notes
You may notice that our commit id is different from yours, even though we made the same change, while the commit ids up to this point have all been the same. That’s because if there is any difference between two commits, including the author or the time it was created, the commits will have different ids.
2.8 Branches
比起那些不易理解的commit ID,創(chuàng)建branch可以更方便人類理解。比如一個program要做一個中文版,那就創(chuàng)建一個branch,起名叫"chinese". 在git中默認(rèn)的最主要的branch是master.
master branch has quilty, so it should be stable. 當(dāng)我們做一些experiment 或是 add new feature的時(shí)候,會創(chuàng)建一個新的branch. Branches are also good when not only collaborating publicly, but they really good to collaborate with yourself.

2.8.1 Making a Branch
git branch
show the current branch, git branch easy-mode
create a new branch with the argument name. * master
表示當(dāng)前所在的branch是master,git checkout easy-mode
switch to the easy-mode branch,
找到game.js
中關(guān)于fragement的代碼,把3
改為2
,這樣游戲里擊中隕石后就會變?yōu)閮砂攵皇侨糠帧?/p>
We consider this one logical change since it changes the behavior of the game.
git add game.js
, git commit -m "Make asteroids split into 2 smaller pieces instead of 3

git status
顯示:
On branch easy-mode
nothing to commit, working directory clean
2.8.2 Branches for Collaboration
通常一個project的workflow是這樣的,一開始有master,然后分為兩個branch,一個bug-fix, 一個feature.這樣可以同時(shí)進(jìn)行debug和add feature的工作。

Then once a feature or bug-fix is complete, the author can either update master to the tip of new branch,

or combine the feature branch with the current master, using git merge feature.

實(shí)戰(zhàn)
We add a new feature to the game, a new game mechanic. you can collect coins by touching them with your ship.
現(xiàn)在老師A已經(jīng)創(chuàng)建了branch coin
, 她告訴老師B可以test it. She can get some feedback before adding it into the main branch.
測試發(fā)現(xiàn)能通過touch coin得分,但是spaceship沒有顏色,git log
后發(fā)現(xiàn)在branch coin里沒有添加顏色的commit Id。git checkout master
,git log
發(fā)現(xiàn)在master里有添加顏色的commit.
Git can help you visualize the branch structure via the command:
git log --graph --oneline master coins
--oneline
means making the output shorter and easier to see.
master coins
means telling the Git which branch I want to visualize
output分為三部分,從下到上分別是:
- These commits existed before the coins branch was created.
- These are commits that Sarah added to the coins branch.
- And these are commits that were added to master after the coins branch was created.

Quiz


2.8.3 Reachability
每一個commit都有自己的parent, 只有一個沒有,the initial one. Using arrow to represent parent.
但是有些commit之間是unreachable的

quiz,簡圖,a and b are branch name.


2.8.4 Detached HEAD Revisited
To understancd the entire detached head message. Remember, to get this message, we checkout a commit, not a branch. Remember the head just means current commit.

Running git checkout -b new_branche_name
is equivalent to running two commands.
First, its just like running git branch new_branch_name
and then, running git checkout
on that new branch.

比如我一直master這條直線上的某個branch做開發(fā),添加了一個new feature, 我想要把這個feature獨(dú)立出來,就用git checkout -b new_branche_name
把這個new feature 從master主線上獨(dú)立出來,head也會teach到這個new branch上,圖中最下面的new_branch_name 就是new branch, 對于其他branch,這個new branch是unreachable的,不論怎么玩都沒關(guān)系。

2.8.5 Combining Simple Files(Mering files)
How to conbine braches into single version?
之前講到了branch coin添加了吃coin的feature,但是spaceship沒有顏色,而branch master的spaceship有顏色,但沒有吃coin的feature,我們要combine these two branch into a new branch.

An example quiz
Jack和Rachel都有B和D,所以我們希望在final version里有這個連feature.但是其他三個都是只有一個人添加了的feature,我們不確定這個feature在final version 里是否是必須的,所以不確定。

那么怎么來確定需要哪個feature呢?如果我們知道original里有哪些feature的話呢?
quiz two
A:Rachel 有A,沒做什么修改,但Jack明確地刪除了A,說明不需要A。
C:Original和Jake都沒有C,但是Rachel added it. 說明it should be in the final.
E: same with C.

2.8.6 Merging Coins into Master
現(xiàn)在branch coins(這個coin只是一個commit ID的易于理解的名稱,一個branch tip,現(xiàn)在這個branch coins 的head teach在ships on conis這個commit上),而master在color這個commit上。我們要把master當(dāng)做主線,把coins添加到里面去。
利用三個commit,git能實(shí)現(xiàn)merge的功能。圖中藍(lán)色畫圈的三個。
revert controls 是Jack和Rechel開始分道揚(yáng)鑣的commit,
ships on coins 是Jack added coins feature
color 是Rechel在master 主線上的更改,包括添加了color等feature.
這個combined version is also a commit. 而這個new commit會store the information of its parent, 其parent就是color和ships on coins. 這樣的merge能保存兩條線上所有的commit.

一旦我們成果merge,就不在需要coins這個branch了,因?yàn)槲覀兛梢杂煤喜⒑蟮膍aster直接回溯到之前的commit.So once we're down with the merge, we can delete the coins branch. 注意我們刪除的是label,而不是commit.(Note that when we talk about deleting braches, we mean deleting the label. The commmits will still be there in the history.) However, if no branches can reach the commit, deleting a branch does effectively discard its commits.
So if you deleted the coins branch without merging it in first, you would essentially be abandoning these commits since they would all become unreachable.

quiz
merge后,git log
能看到哪些commits?
只有右下角哪個easy是unreachable.

2.8.7 Merging on the Command Line(實(shí)操)
Comparing a commit to its parent
The command Caroline mentions to compare a commit to its parent is git show commit_id
.也就是說不用非得找到兩個commit ID一起提交,只要找到一個有bug的ID,就能看到它和它parent之間的差別。
Checking out the coins branch
If you haven't already checked out the coins branch, you'll need to do so now with the command git checkout coins
before you'll be able to refer to it. Once you've done that, decide whether you should keep it checked out or check out a different branch before completing the merge.
A note aboutgit merge
下面一大堆就是想說,在git merge
之前,一定要想checkout切換到要merge的branch上,不然會把current branch也merge掉。比如,git checkout branch1
and then type git merge branch2
. The only reason to type git merge branch1 branch2
is if it helps you keep better mental track of which branches you are merging.
git merge
will also include the currently checked-out branch in the merged version. So if you have branch1 checked out, and you run git merge branch2 branch3
, the merged version will combine branch1 as well as branch2 and branch3. That’s because the branch1 label will update after you make the merge commit, so it’s unlikely that you didn’t want the changes from branch1 included in the merge. For this reason, you should always checkout one of the two branches you’re planning on merging before doing the merge. Which one you should check out depends on which branch label you want to point to the new commit.
Since the checked-out branch is always included in the merge, you may have guessed that when you are merging two branches, you don't need to specify both of them as arguments to git merge
on the command line. If you want to merge branch2 into branch1, you can simply git checkout branch1
and then type git merge branch2
. The only reason to type git merge branch1 branch2
is if it helps you keep better mental track of which branches you are merging.
Also, since the two branches are merged, the order in which they are typed into the command line does not matter. The key is to remember that git merge
always merges all the specified branches into the currently checked out branch, creating a new commit for that branch.
Merge conflict
如果出現(xiàn)這個confilict,到時(shí)候在查看這個網(wǎng)站,看教程
If you get a message like this
Auto-merging game.js
CONFLICT (content): Merge conflict in game.js
Automatic merge failed; fix conflicts and then commit the result.
必須先checkout到master, 再merge coins. merge后,會有up arrow,提示你可以git push
,把local推送到遠(yuǎn)端服務(wù)器。如果想要Undo this merge, 用命令git reset --hard commit_sha
。commit_ID
是merge前的一個commit ID.

merge后,會有不同的author,不同的branch線,組合在一起,用git log
很難找到某個commit的parent。這個時(shí)候我們用本小結(jié)一開是介紹的git show commit_ID
來查看某ID和其parent的區(qū)別。
merge成功后就可以delete the branch coins, 用git branch -d coins
。
用git log --graph --oneline
查看commit信息。
2.8.8 Merge Conflicts
orinical里B是原版,但Jake里的B'是修改過的,而Rachel里的B''也是修改過的。如果merge的話,最后的文件里究竟是哪個B

結(jié)果是不確定

2.8.9 Conflict Detection
how Git know whether there is a merge conflict?
舉例來說,有兩個文件。第一個文件foo.py
,第二個文件名字不同,實(shí)現(xiàn)方法不同,但功能一樣。

Consider the following two examples. In both cases, we start with two identical copies of the same file. In the first case, two different contributors add new functions to the bottom of the files. These are different functions that don't interact with each other and have nothing to do with one another. However, in the second case, two different contributors add different implementations of the same function with different names.
Git無法分辨第二個例子里哪個文件應(yīng)該保留。
In the first case, you pretty clearly want both functions to be included, but in the second situation, you probably only want one version of the function, probably whichever one is either more memory efficient or
faster, depending on what you're going for. But Git can't really tell these two options apart.
所以Git不會管那么多閑事,它會把有confilct的文件提醒給contributor,讓這些人自己去決定選擇哪個文件。
Git just assumes that if you're merging together two commits that have changes in the same general area, the authors will want to know about it and have the chance to figure out for themselves which change to keep. This decision to ask the user whenever there's any ambiguity at all does sometimes lead to situations where it seems really obvious to you, as an expert on the content, how to resolve the conflict. But Git brings it to your attention anyway. While this may be annoying, it's significantly better than if Git tried to guess too often, which could lead to weird conglomerate changes that don't really make any sense and probably wouldn't compile or run.
2.8.10 Update Easy Mode
Motivation
Master has updated since the easy-mode branch was created. In this case, it has the addition of coins, which you might like to include in the easy-mode branch. In general, it’s very common that if you make a branch, either an experimental branch or to work on a new feature, you want to periodically merge master into that branch. This is because master usually contains the official version of the code, and it’s common to want experimental changes to include all of the changes to master.
Previous version of the diagram
Here’s what the history looks like right now.

Draw an updated diagram
If you merge master into the easy-mode branch, what will the history look like afterward? Take a minute to draw the new history using whatever method you like best. You might want to use pencil and paper, or create a text file with stars and dashes similar to the output of git log --graph
, or maybe use an online diagramming tool like gliffy or yUML. Once you’re finished, watch the solution to compare your diagram to Sarah’s.
Solution

2.8.11 Resolving Merge Conflicts
merge master 和 easy-mode的時(shí)候,出現(xiàn)了merge conflict, 因?yàn)閮蓚€branch都對game.js
做了更改。

打開game.js
,GIT會把有問題的部分標(biāo)記出來。一些special lines會把內(nèi)容分為三個部分。
The top section, marked HEAD is my code. The bottom section, marked master is the code that's in master right now. And the middle section is the original version that both branches modified,
which git called the common ancestor.
the HEAD and common ancestor

the master

如果解決?
Now when I'm trying to resolve a merge conflict, the first thing I do is try to understand what changes both branches have made. The difference between the middle section and the top, shows the changes that I made in the easy-mode branch. If I'd forgotten what those changes were, I can spend a few minutes comparing and recall that the difference was changing this three to a two.
The difference between the middle and the bottom shows the changes that were made in master.
這樣的conflict在merge的時(shí)候很常見,發(fā)現(xiàn)后可以直接和另一個contributor交談看怎么解決問題,但是我們最好先自己看一下這些difference. it looks like Sarah replaced this entire section of code with a call to a function called breakIntoFragments. Now that probably means that she created this function and moved this code into that function. Which is a pretty common way to make code more readable by breaking it up into understandable parts.
Now it might look like this function existed in both versions. Since it's not inside the special merge conflict lines. But actually, that's just because this change didn't create a conflict. Now after I've spent a few minutes comparing this function to the code that was removed by Sarah below.
I could see that she didn't make any changes to the code, other than to move it.
Now, how should I reserve this merge conflict?
For example, if I wanted to undo my changes, I could just delete my version and the original version, as well as these special lines to leave only Sarah's code. Instead, you should create a version of the code that incorporates both of our changes.
2.8.12 Resolving Merge Conflicts Quiz
conflict file
Asteroid = function() {
this.breakIntoFragments = function () {
for (var i = 0; i < 3; i++) {
var roid = $.extend(true, {}, this);
roid.vel.x = Math.random() * 6 - 3;
roid.vel.y = Math.random() * 6 - 3;
if (Math.random() > 0.5) {
roid.points.reverse();
}
roid.vel.rot = Math.random() * 2 - 1;
roid.move(roid.scale * 3); // give them a little push
Game.sprites.push(roid);
}
};
this.collision = function (other) {
SFX.explosion();
if (other.name == "bullet") Game.score += 120 / this.scale;
this.scale /= 3;
if (this.scale > 0.5) {
<<<<<<< HEAD
// break into fragments
for (var i = 0; i < 2; i++) {
var roid = $.extend(true, {}, this);
roid.vel.x = Math.random() * 6 - 3;
roid.vel.y = Math.random() * 6 - 3;
if (Math.random() > 0.5) {
roid.points.reverse();
}
roid.vel.rot = Math.random() * 2 - 1;
roid.move(roid.scale * 3); // give them a little push
Game.sprites.push(roid);
}
||||||| merged common ancestors
// break into fragments
for (var i = 0; i < 3; i++) {
var roid = $.extend(true, {}, this);
roid.vel.x = Math.random() * 6 - 3;
roid.vel.y = Math.random() * 6 - 3;
if (Math.random() > 0.5) {
roid.points.reverse();
}
roid.vel.rot = Math.random() * 2 - 1;
roid.move(roid.scale * 3); // give them a little push
Game.sprites.push(roid);
}
=======
this.breakIntoFragments();
>>>>>>> master
}
};
};
solution
In this case I want to keep Sarah's change of moving this code to the function.
So I'll need to make my change to the function.
So, I'll scroll back up to the function and change this 3 to a 2 (line 4).
Now I'll delete my version since I already have the change from it and the original version since I don't need that version any more and I'll delete git's special lines.
I'll also clean up a little bit by removing these blank lines. Now I'm left with the call to the breakIntoFragments function, which breaks each asteroid into two fragments, rather than three.
changed file:
Asteroid = function() {
this.breakIntoFragments = function () {
for (var i = 0; i < 2; i++) { //change 3 to 2
var roid = $.extend(true, {}, this);
roid.vel.x = Math.random() * 6 - 3;
roid.vel.y = Math.random() * 6 - 3;
if (Math.random() > 0.5) {
roid.points.reverse();
}
roid.vel.rot = Math.random() * 2 - 1;
roid.move(roid.scale * 3); // give them a little push
Game.sprites.push(roid);
}
};
this.collision = function (other) {
SFX.explosion();
if (other.name == "bullet") Game.score += 120 / this.scale;
this.scale /= 3;
if (this.scale > 0.5) {
// break into fragments
for (var i = 0; i < 2; i++) {
var roid = $.extend(true, {}, this);
roid.vel.x = Math.random() * 6 - 3;
roid.vel.y = Math.random() * 6 - 3;
if (Math.random() > 0.5) {
roid.points.reverse();
}
roid.vel.rot = Math.random() * 2 - 1;
roid.move(roid.scale * 3); // give them a little push
Game.sprites.push(roid);
}
this.breakIntoFragments();
}
};
};
2.8.13 Committing the Conflict Resolution
我想保留branch easy-mode, 所以merge master into easy-mode. 先checkout easy-mode, 再merge. 發(fā)現(xiàn)有conflict,我們按照上一節(jié)的教程,修改好game.js
。

更改完成后,git status
, 顯示 fix conflict. git add game.js
后,再git status
, 顯示 you are still merging.

git commit -m "fix merge confict, merge master into easy-mode"
, 不知道怎么設(shè)置能像教程里那樣自動彈出寫message的editor界面,所以只好自己寫message,要盡量寫的詳細(xì)些。
git log --graph --oneline
:


quiz
git log -n 1
只輸出一個commit,更改數(shù)字1
可以得到不同數(shù)量的commit

output:

2.9 Concept Map: branch, merge
You know that merging and branching both have to do with commits, but what kind of relationships does each of these ideas have with the commit.
Branches are really just labels that refer to commits, so we have this new kind of relationship, through first year relationship. So this has to be branch. Let's make sure this really makes sense.
Does diff really have an operates on relationship with branch?
Well, yes.You can diff two commits or you can diff two branches, which is basically just diff into two commits. But they're sort of different ideas, similarly you can run log on a branch or on a commit.
So let's check this one, does merge operate on commits?
Well, yeah, merge takes two commits and sort of smushes them together into a new commit.
