Cho-Ching's Blog

[Git] basics -- Recording Changes to the Repository

以下部份翻譯ProGit 中文版v1 / 英文版v2, 加上自己一些測試。

Distributed Version Control System(DVCS)

In a DVCS, client don't just check out the latest snapshot of the files; they fully mimrror the repository.

Thus if any server dies, any of the client repositories can be copied back up to the server to restore it.

Every clone is really a full backup of all the data.

The three states

Every time you commit, or save the state of your project in Git, it is basically takes a picture of what all your files look like at that moment and stores a reference to that snapshot.

To be efficient, if files not changed, Git doesn't store the file again, just a link to the previous identical file it has already stored.

術語專有名詞要記, 這樣log, command才看的懂

Git has three main states that your files can reside in: committed, modified, and staged.

Committed: the data is safely stored in your local database.

Modified: you have changed the file but have not committed it to your database yet.

Staged: you have marked a modified file in its current version to go into your next commit snapshot.

This leads us to the three main sections of a Git project: the Git directory, the working directory, and the staging area

git

Git directory is where Git stores the metadata and object database for your project. it is what is copied when you clone a repository from another computer.

Working directory is a single checkout of one version of the project. These files are pulled out of the compressed database in the git directory and placed on disk for you to use or modify.

工作目錄是專案被取出的某一個版本。 這些檔案從Git目錄內被壓縮過的資料庫中拉出來並放在磁碟機供讀者使用或修改。

Staging area(暫存區) is a file, generally contained in your Git directory, that stories information about what will go into your next commit. it's sometimes referred to as the "index", but it's also common to refer to it as the staging area

暫存區域是一個單純的檔案,一般來說放在Git目錄,儲存關於下一個提交的資訊。 有時稱為索引(index)

在Git help doc中, 都會稱做像是working tree, index tree這樣。

First-time Git setup

git config :

$ git config --global user.name "Lu Cho-Ching"
$ git config --global user.email choching.work@gmail.com
$ git config --global core.editor vim

~/.gitconfig global 設定所在檔案:

[user]
  name = Lu Cho-Ching
  email = choching.work@gmail.com
[core]
  editor = vim

.git/config local設定所在檔案:

[core]
  repositoryformatversion = 0
  filemode = true
  bare = false
  logallrefupdates = true
[branch "master"]
[remote "origin"]
  url = git@github.com:luchoching/myJblog.git
  fetch = +refs/heads/*:refs/remotes/origin/*

git config -l 列出global + local設定

Getting a Git repository

git init : initializing a repository in an existing directory

git clone: getting a copy of an existing Git repository

Recording Changes to the Repository

The lifecycle if the status of your files:

lifecycle

git clone 開始狀態

git clone某個repository回來的情況:

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

nothing to commit, working directory clean

所有檔案都是被追縱(tracked) 並且未修改的(unmodified), 因為都還沒有檔案開始修改新增, 所以: working directory clean.

如果新增一個檔案:

$ echo "a" > a.txt
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)

  a.txt

nothing added to commit but untracked files present (use "git add" to track)

會告知該檔案屬於untracked files。

git init 開始狀態

git init後的git status如下:

$ git status
On branch master

Initial commit

nothing to commit (create/copy files and use "git add" to track)

新增一個檔案的git狀態:

$ echo "my project" > README
$ git status
On branch master

Initial commit

Untracked files:
  (use "git add <file>..." to include in what will be committed)

  README

nothing added to commit but untracked files present (use "git add" to track)

提示目前有untracked files。

Untracked意思代表說Git有看到這個檔案, 但是這個檔案在先前的snapshoot(commit)還沒有出現過。

除非我們明確的告訴Git要commit snapshots, 不然Git不會主動做這件事情, 主要就是避免我們把不必要包含的檔案commit進去snashoot(例如binary files)

tracking new files

$ git add README
$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

  new file:   README

這時候檔案被歸類在Changes to be committed, 也就是staged(暫存)的狀態, 這個檔案已經被暫存起來, 若這個時候執行git commit, 那這個時候的這個檔案的版本將會被加到新的snapshoot中。

git add可加檔案或是目錄。

staging modified files

$ echo "123" >> README 
$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

  new file:   README

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

  modified:   README

歸類為Changes not stagged for commit的表示在這working direcoty的檔案已經被追縱(tracked)應並且已經被修改(modified), 但是還沒被暫存(staged)

注意這個時候commit的話, 就會把我們最後git add的內容commit,而不是把現在在working directory看到的結果commit。

再度git add就會把修改過的內容暫存:

On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

  new file:   README

git add為多用途指令, 可以用來tracking new files, stage files, 也可以用來處理merge檔案問題, 所以要把git add當作是add this conetent to the next commit, 而不是把這個檔案加到project中

可以git add file1, file2..., git add dir, git add .

git add -p 可以讓我們逐步選擇哪個檔案要add, 並且會列出差異讓我們判斷。

git status -s

gss-s

??表示untracked new file, A表示stagged new file, 紅色M表示tracked,但修改內容還未staged, 右邊綠色M表示tracked, 修改內容也已經staged

.gitignore

# no .a files
*.a

# but do track lib.a, even though you're ignoring .a files above
!lib.a

# only ignore the TODO file in the current directory, not subdir/TODO
/TODO

# ignore all files in the build/ directory
build/

# ignore doc/notes.txt, but not doc/server/arch.txt
doc/*.txt

# ignore all .pdf files in the doc/ directory
doc/**/*.pdf

Viewing your Staged and Unstaged Changes

git status顯示:

git diff顯示:

git diff比較我們在working directory跟staging area的檔案, 結果告知哪些行改變了但還沒staged:

$ git diff
diff --git a/Rakefile b/Rakefile
index 8f94139..f14abb9 100644
--- a/Rakefile
+++ b/Rakefile
@@ -2,6 +2,8 @@ require 'rubygems'
 Gem::manage_gems
 require 'rake/gempackagetask'

+test = 1
+
 spec = Gem::Specification.new do |s|
     s.platform  =   Gem::Platform::RUBY
     s.name      =   "simplegit"

git diff --stat 僅列出摘要:

$ git diff --stat
 Rakefile | 2 ++
 1 file changed, 2 insertions(+)

git diff --staged 顯示我們目前staged 和最後的commit的差異。 例如:

$ git status
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   README

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

  modified:   README
  modified:   a.txt

狀態顯示README和a.txt都修改但修改的部份還沒staged, 利用git diff:

$ git diff
diff --git a/README b/README
index 330ce48..53d07a8 100644
--- a/README
+++ b/README
@@ -1,3 +1,4 @@
 my project
 123
 abc
+1q2w
diff --git a/a.txt b/a.txt
index 72943a1..730873b 100644
--- a/a.txt
+++ b/a.txt
@@ -1 +1,2 @@
 aaa
+567

git add README, git diff剩下顯示a.txt:

$ git add README
$ git diff
diff --git a/a.txt b/a.txt
index 72943a1..730873b 100644
--- a/a.txt
+++ b/a.txt
@@ -1 +1,2 @@
 aaa
+567

繼續察看README staged和上次commit的差異:

$ git diff --staged README
diff --git a/README b/README
index 264e6ae..53d07a8 100644
--- a/README
+++ b/README
@@ -1,2 +1,4 @@
 my project
 123
+abc
+1q2w

--word-diff

git diff --word-diff:

  [-delay(1000);-]{+delay(3000);+}               // wait for a second

git diff:

-  delay(1000);               // wait for a second
+  delay(3000);               // wait for a second

Commit your changes

當我們的staging area已經調整到我們想要的結果了,就可以commit changes(提交變更的部份)。

所有都還沒有git add的內容都不會commit。

$ git commit -m 'remove 2 lines'
[master 6e4bee4] remove 2 lines
 1 file changed, 2 deletions(-)

訊息顯示commit到mater分支, SHA-1檢查碼(6e4bee4), 改了多少檔案, 有幾行被新增刪除

每次我們執行一次commit, 我們就紀錄了一個我們專案的snapshot, 可以用來回復或是比對之前之後的紀錄。

git commit -a: commit a snapshot of all changes in the working directory. (不包含新增檔案)

commit messaage很重要 , How to Write a Git Commit Message 做了很詳細的提醒(more!)

Removing Files

要從Git移除一個檔案, 必須要從你的tracked file中移除。更明確的說, 就是從你的staging area中移除, 並且執行commit。

git rm 會將這個檔案從working directory中移除, 因此下回你也不會看到這個檔案作為untracked file 出現! 

例如:

$ git rm README
rm 'README'

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

  deleted:    README

$ git commit -m 'del README again'
[master b8cf1eb] del README again
 1 file changed, 1 deletion(-)
 delete mode 100644 README

當執行git rm README的時候, README已經從working directory刪除了, README的狀態會被歸類為Changes to be committed, 我們必須要git commit讓這個修改紀錄commit到snapshot中。

那我們先刪除(rm README)某檔案(也就是將該檔案從working directory中移除)的話:

$ rm README
$ git status
On branch master
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

  deleted:    README

這個檔案會被歸類為Changes not staged for commit, 必須執行git rm README或是git add README:

$ git rm README
rm 'README'
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

  deleted:    README

下一次commit時, 該檔案就會消失不再追蹤。

強制移除已經staged的檔案

例如:

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

  modified:   d.json

$ git rm d.json 
error: the following file has changes staged in the index:
    d.json
(use --cached to keep the file, or -f to force removal)

git rm的時候會告知該檔案已經在stage, 真的不想要,就使用git rm -f來強制移除:

$ git rm -f d.json
rm 'd.json'
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

  deleted:    d.json

這樣避免不小心把已經stage但是還未commit到snapshot的檔案誤殺了。

將檔案從stage移出, 但是不想再追蹤它

也就是說, 我想把檔案保留在硬碟裡, 但是不再讓Git追蹤它。

使用git rm --cached d.json:

$ git rm --cached d.json 
rm 'd.json'
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

  deleted:    d.json

Untracked files:
  (use "git add <file>..." to include in what will be committed)

  d.json

會發現d.json檔案還保留在working directory ,但是歸類為Untracked files, 另外Changes to be committed這邊也有deleted: d.json的紀錄提供commit, 方便日後復原。

Moving Files

$ git mv file_from file_to

例如:

$ git mv a.txt aa.txt
$ gss
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

  renamed:    a.txt -> aa.txt

More

How to Write a Git Commit Message

<< 回到文章列表