How to Use Git and GitHub(一)

因?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

git1.png-268.6kB
git1.png-268.6kB

in windows


git2.png-389.8kB
git2.png-389.8kB

in Linux and Mac


git3.png-492.9kB
git3.png-492.9kB

1.2 Reflections

git4.png-454.1kB
git4.png-454.1kB

1.3 Properties of a VCS for Code

VCS:Version Control System


git5.png-509.8kB
git5.png-509.8kB

1.4 Manual Commits in Git

這個Commit的概念在git中很重要

git6.png-229.2kB
git6.png-229.2kB

1.5 Creating a Concept Map

git7.png-306.2kB
git7.png-306.2kB

1.6 Using Git to View History

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

git8.png-664.4kB
git8.png-664.4kB

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

git9.png-436.1kB
git9.png-436.1kB

1.7 Concept Map: diff

git10.png-362.5kB
git10.png-362.5kB

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

git11.png-131.4kB
git11.png-131.4kB

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

git12.png-340.3kB
git12.png-340.3kB

1.9.1 Git Commits Across Multiple Files

git13.png-229.9kB
git13.png-229.9kB

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.

git14.png-456.2kB
git14.png-456.2kB

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

git log --stat
git15.png-480.5kB
git15.png-480.5kB

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

git16.png-587.3kB
git16.png-587.3kB

1.10 Cloning and Exploring The Repo

git17.png-396kB
git17.png-396kB

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

git18.png-290kB
git18.png-290kB
  • Cloning a Repository
    To clone a repository, run git clone followed by a space and the repository URL.
  • Exiting git log
    To stop viewing git log output, press q (which stands for quit).
  • Getting Colored Output
    To get colored diff output, run git config --global color.ui auto
  • Using git log and git diff
    As a reminder, running git log will show a list of the recent commits with information about them, including commit IDs. Running git 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

git19.png-352.2kB
git19.png-352.2kB
git20.png-358.7kB
git20.png-358.7kB

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.

示意圖


git21.png-338kB
git21.png-338kB

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

git22.png-102.4kB
git22.png-102.4kB

回到那個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.

git23.png-683.5kB
git23.png-683.5kB

再次打開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é)果,重建出另一個文件。

git24.png-35.2kB
git24.png-35.2kB

2 Tracking Versions Using Git Quiz

git25.png-82.4kB
git25.png-82.4kB

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

git26.png-78.3kB
git26.png-78.3kB

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

git27.png-106.9kB
git27.png-106.9kB

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多了什么,少了什么。 順序很重要。

git27.png-106.9kB
git27.png-106.9kB

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

git29.png-73.8kB
git29.png-73.8kB

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

git30.png-225.1kB
git30.png-225.1kB

2.2 Initializing a Repository

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

git31.png-290.8kB
git31.png-290.8kB

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.

git32.png-222.2kB
git32.png-222.2kB

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.

git34.png-343.8kB
git34.png-343.8kB

2.3 Staging Area

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

git35.png-319kB
git35.png-319kB
git36.png-285.5kB
git36.png-285.5kB
git37.png-444.3kB
git37.png-444.3kB

2.4 Concept Map: init, add, staging area

git38.png-319.9kB
git38.png-319.9kB

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)

  1. 打開directory asteroids 的時(shí)候,發(fā)現(xiàn)當(dāng)前的ID是之前的一個ID,不是最新的3884eab839。而且game.js也被modify了。發(fā)現(xiàn)必須得commit or discard the modification of game.js,才能checkout回到3884eab839。于是用git checkout game.js discard the modification, and then git checkout 3884eab839回到初始狀態(tài)?,F(xiàn)在這個狀態(tài)是最開始clone完的狀態(tài),有bug。
  2. 我們已經(jīng)知道了有bug的ID是25ede836 with the message "a couple missing ends with the ipad verison", the previous one is df035382。用git diff df035382 25ede836找到不同的地方。
    git39.png-118.4kB
    git39.png-118.4kB
    記住刪除信息所在位置,用sublime打開game.js,加上刪去的那一行。用git status檢查,看到modified: game.js.
  3. st index.html 打開后隨便找一行collapse the line onto the previous one.
  4. 好了。我們現(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)。

  1. The repository contains several commits. And each commit contains several files. 比如現(xiàn)在圖中的the most recent commit contain the game.js and index.html,這些是ID 3884eab839 下的文件,也就是說有bug的文件。修改的文件還在working directory.
  2. The staging area is a copy of the most recent commit until I add changes to it. So it has those same files.
  3. The working directory also has the same files in it, but I've made some updates to game.js and index.html,which I'll represet using these stars.
git40.png-250.8kB
git40.png-250.8kB

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.

git43.png-393kB
git43.png-393kB

(注意兩個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:

git41.png-101.3kB
git41.png-101.3kB
  • add game.js to the staging area and run git 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

git44.png-476.5kB
git44.png-476.5kB

run git diff --staged:

git45.png-54.4kB
git45.png-54.4kB
  • git commit -m "Add delay back to bullets", then git diff

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

git46.png-103.5kB
git46.png-103.5kB
  • 但如果我不想要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.

git47.png-523.9kB
git47.png-523.9kB

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

git48.png-393.9kB
git48.png-393.9kB

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.

git50.png-313.6kB
git50.png-313.6kB

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

git51.png-452.7kB
git51.png-452.7kB

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的工作。

git52.png-326.3kB
git52.png-326.3kB

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

git53.png-323.6kB
git53.png-323.6kB

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

git54.png-209.7kB
git54.png-209.7kB

實(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分為三部分,從下到上分別是:

  1. These commits existed before the coins branch was created.
  2. These are commits that Sarah added to the coins branch.
  3. And these are commits that were added to master after the coins branch was created.
git56.png-780.6kB
git56.png-780.6kB

Quiz

git57.png-162.9kB
git57.png-162.9kB
git58.png-312.4kB
git58.png-312.4kB

2.8.3 Reachability

每一個commit都有自己的parent, 只有一個沒有,the initial one. Using arrow to represent parent.

但是有些commit之間是unreachable的

git59.png-408.2kB
git59.png-408.2kB

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

git60.png-326.1kB
git60.png-326.1kB
git61.png-370.8kB
git61.png-370.8kB

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.

git62.png-414.3kB
git62.png-414.3kB

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.

git63.png-473.5kB
git63.png-473.5kB

比如我一直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)系。

git64.png-464.8kB
git64.png-464.8kB

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.

git65.png-405.8kB
git65.png-405.8kB

An example quiz

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

git66.png-365.4kB
git66.png-365.4kB

那么怎么來確定需要哪個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.

git67.png-370.8kB
git67.png-370.8kB

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就是colorships on coins. 這樣的merge能保存兩條線上所有的commit.

git69.png-437.3kB
git69.png-437.3kB

一旦我們成果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.

git70.png-573.2kB
git70.png-573.2kB

quiz
merge后,git log能看到哪些commits?

只有右下角哪個easy是unreachable.

git71.png-436kB
git71.png-436kB

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.

git72.png-115.1kB
git72.png-115.1kB

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

git73.png-265.3kB
git73.png-265.3kB

結(jié)果是不確定


git74.png-385.7kB
git74.png-385.7kB

2.8.9 Conflict Detection

how Git know whether there is a merge conflict?

舉例來說,有兩個文件。第一個文件foo.py,第二個文件名字不同,實(shí)現(xiàn)方法不同,但功能一樣。

git75.png-229.8kB
git75.png-229.8kB

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.

git76.png-106kB
git76.png-106kB

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

git77.png-394.4kB
git77.png-394.4kB

2.8.11 Resolving Merge Conflicts

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

git78.png-367.9kB
git78.png-367.9kB

打開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


git79.png-617.8kB
git79.png-617.8kB

the master


git80.png-556.6kB
git80.png-556.6kB

如果解決?

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。

git82.png-64.2kB
git82.png-64.2kB

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

git83.png-439.9kB
git83.png-439.9kB

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

git log --graph --oneline

git85.png-65.1kB
git85.png-65.1kB
git86.png-192.7kB
git86.png-192.7kB

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

git88.png-86.6kB
git88.png-86.6kB

output:

git89.png-57.2kB
git89.png-57.2kB

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.

git90.png-478.9kB
git90.png-478.9kB
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容