[轉] git reset簡介

http://blog.csdn.net/hudashi/article/details/7664464html

http://guibin.iteye.com/blog/1014369git

http://web.mit.edu/~mkgray/project/silk/root/afs/sipb/project/git/git-doc/git-reset.html
web

1、基本篇
在git的通常使用中,若是發現錯誤的將不想staging的文件add進入index以後,想回退取消,則可使用命令:git reset HEAD <file>...,同時git add完畢以後,git也會作相應的提示,好比:
引用
# Changes to be committed: 
#   (use "git reset HEAD <file>..." to unstage) 
# new file:   Test.scala 
git reset [--hard|soft|mixed|merge|keep] [<commit>HEAD]:將當前的分支重設(reset)到指定的<commit>或者HEAD(默認,若是不顯示指定commit,默認是HEAD,即最新的一次提交),而且根據[mode]有可能更新index和working directory。mode的取值能夠是hard、soft、mixed、merged、keep。下面來詳細說明每種模式的意義和效果。
A). --hard:重設(reset) index和working directory,自從<commit>以來在working directory中的任何改變都被丟棄,並把HEAD指向<commit>。 
具體一個例子,假設有三個commit, git st:
commit3: add test3.c
commit2: add test2.c
commit1: add test1.c
執行git reset --hard HEAD~1後,
顯示:HEAD is now at commit2,運行git log
commit2: add test2.c
commit1: add test1.c
運行git st, 沒有任何變化
B). --soft:index 和working directory中的內容不做任何改變,僅僅把HEAD指向<commit>。這個模式的效果是,執行完畢後,自 從<commit>以來的全部改變都會顯示在git status的"Changes to be committed"中。 
具體一個例子,假設有三個commit, git st:
commit3: add test3.c
commit2: add test2.c
commit1: add test1.c
執行git reset --soft(默認) HEAD~1後,運行git log
commit2: add test2.c
commit1: add test1.c
運行git status, 則test3.c處於暫存區,處於準備提交狀態。即此時git commit就會提交它。
  在使用git進行協做開發時,咱們常常須要將本身的修改生成patch發給被人,可是在修改代碼的過程當中咱們進行了不少次的提交,如何生成從最初的代碼狀態到最終代碼狀態的patch呢?下面要介紹的功能是應對這中狀況。
現假設咱們git軟件倉庫中的分支狀況以下:
a-->b-->c
也就是說咱們的代碼從狀態a修改到狀態b,進行 一次提交,而後再修改到狀態c,進行一次提交。這時咱們已經確定由a到c的修改是正確的,再也不須要狀態b了,而且要把從a到c的變化生成一個patch發 送給別人。若是直接打包的話會生成兩個path,那麼如何生成一個patch呢,這時就須要git-reset命令。
首先給狀態a建立一個tag,假設名稱爲A,而後執行
git-reset --soft A
這樣咱們的軟件倉庫就變爲
a
狀態b和狀態c都已經被刪除了,可是當前的代碼並無被改變,仍是狀態c的代碼,這時咱們作一次提交,軟件倉庫變成下面的樣子:
a-->d
狀態d和狀態c所對應的代碼是徹底相同的,只是名字不一樣。如今就能夠生成一個patch打包發給別人了
C). --mixed:僅reset index,可是不reset working directory。這個模式是默認模式,即當不顯示告知git reset模式時,會使用mixed模式。這個模式的效果是,working directory中文件的修改都會被保留,不會丟棄,可是也不會被標記成"Changes to be committed",可是會打出什麼還未被更新的報告。報告以下: 
引用
Unstaged changes after reset: 
M Test.Scala 
M test.txt

D). --merge和--keep用的很少,在下面的例子中說明。 
2、經常使用示例
下面列出一些git reset的典型的應用場景: 
A) 回滾add操縱 
引用
$ edit                                     (1) 
$ git add frotz.c filfre.c 
$ mailx                                    (2) 
$ git reset                                (3) 
$ git pull git://info.example.com/ nitfol  (4) 

(1) 編輯文件frotz.c, filfre.c,作了些更改,並把更改添加到了index 
(2) 查看郵件,發現某人要你pull,有一些改變須要你merge下來 
(3) 然而,你已經把index搞亂了,由於index同HEAD commit不匹配了,可是你知道,即將pull的東西不會影響已經修改的frotz.c和filfre.c,所以你能夠revert這兩個文件的改變。 revert後,那些改變應該依舊在working directory中,所以執行git reset。 
(4) 而後,執行了pull以後,自動merge,frotz.c和filfre.c這些改變依然在working directory中。 

B) 回滾最近一次commit 
引用
$ git commit ... 
$ git reset --soft HEAD^      (1) 
$ edit                        (2) 
$ git commit -a -c ORIG_HEAD  (3) 

(1) 當提交了以後,你又發現代碼沒有提交完整,或者你想從新編輯一下提交的comment,執行git reset --soft HEAD^,讓working tree還跟reset以前同樣,不做任何改變。 
HEAD^指向HEAD以前最近的一次commit。 
(2) 對working tree下的文件作修改 
(3) 而後使用reset以前那次commit的註釋、做者、日期等信息從新提交。注意,當執行git reset命令時,git會把老的HEAD拷貝到文件.git/ORIG_HEAD中,在命令中可使用ORIG_HEAD引用這個commit。 commit 命令中 -a 參數的意思是告訴git,自動把全部修改的和刪除的文件都放進stage area,未被git跟蹤的新建的文件不受影響。commit命令中-c <commit> 或者 -C <commit>意思是拿已經提交的commit對象中的信息(做者,提交者,註釋,時間戳等)提交,那麼這條commit命令的意思就很是 清晰了,把全部更改的文件加入stage area,並使用上次的提交信息從新提交。 

C) 回滾最近幾回commit,並把這幾回commit放到叫作topic的branch上去。 
引用
$ git branch topic/wip     (1) 
$ git reset --hard HEAD~3  (2) 
$ git checkout topic/wip   (3)

(1) 你已經提交了一些commit,可是此時發現這些commit還不夠成熟,不能進入master分支,但你但願在新的branch上潤色這些commit改動。所以執行了git branch命令在當前的HEAD上創建了新的叫作 topic/wip的分支。 
(2) 而後回滾master branch上的最近三次提交。HEAD~3指向當前HEAD-3個commit的commit,git reset --hard HEAD~3即刪除最近的三個commit(刪除HEAD, HEAD^, HEAD~2),將HEAD指向HEAD~3。 

D) 永久刪除最後幾個commit 
引用
$ git commit ... 
$ git reset --hard HEAD~3   (1)

(1) 最後三個commit(即HEAD, HEAD^和HEAD~2)提交有問題,你想永久刪除這三個commit。 

E) 回滾merge和pull操做 
引用
$ git pull                         (1) 
Auto-merging nitfol 
CONFLICT (content): Merge conflict in nitfol 
Automatic merge failed; fix conflicts and then commit the result. 
$ git reset --hard                 (2) 
$ git pull . topic/branch          (3) 
Updating from 41223... to 13134... 
Fast-forward 
$ git reset --hard ORIG_HEAD       (4)

(1) 從origin拉下來一些更新,可是產生了不少衝突,你暫時沒有這麼多時間去解決這些衝突,所以你決定稍候有空的時候再從新pull。 
(2) 因爲pull操做產生了衝突,所以全部pull下來的改變還沒有提交,仍然再stage area中,這種狀況下git reset --hard 與 git reset --hard HEAD意思相同,即都是清除index和working tree中被搞亂的東西。 
(3) 將topic/branch合併到當前的branch,此次沒有產生衝突,而且合併後的更改自動提交。 
(4) 可是此時你又發現將topic/branch合併過來爲時尚早,所以決定退滾merge,執行git reset --hard ORIG_HEAD回滾剛纔的pull/merge操做。說明:前面講過,執行git reset時,git會把reset以前的HEAD放入.git/ORIG_HEAD文件中,命令行中使用ORIG_HEAD引用這個commit。一樣 的,執行pull和merge操做時,git都會把執行操做前的HEAD放入ORIG_HEAD中,以防回滾操做。 

F) 在被污染的working tree中回滾merge或者pull 
引用
$ git pull                         (1) 
Auto-merging nitfol 
Merge made by recursive. 
nitfol                |   20 +++++---- 
... 
$ git reset --merge ORIG_HEAD      (2)

(1) 即使你已經在本地更改了一些你的working tree,你也可安全的git pull,前提是你知道將要pull的內容不會覆蓋你的working tree中的內容。 
(2) git pull完後,你發現此次pull下來的修改不滿意,想要回滾到pull以前的狀態,從前面的介紹知道,咱們能夠執行git reset --hard ORIG_HEAD,可是這個命令有個反作用就是清空你的working tree,即丟棄你的本地未add的那些改變。爲了不丟棄working tree中的內容,可使用git reset --merge ORIG_HEAD,注意其中的--hard 換成了 --merge,這樣就能夠避免在回滾時清除working tree。 

G) 被中斷的工做流程 
在實際開發中常常出現這樣的情形:你正在開發一個大的feature,此時來了一個緊急的bug須要修復,可是目前在working tree中的內容尚未成型,還不足以commit,可是你又必須切換的另外的branch去fix bug。請看下面的例子 
引用
$ git checkout feature ;# you were working in "feature" branch and 
$ work work work       ;# got interrupted 
$ git commit -a -m "snapshot WIP"                 (1) 
$ git checkout master 
$ fix fix fix 
$ git commit ;# commit with real log 
$ git checkout feature 
$ git reset --soft HEAD^ ;# go back to WIP state  (2) 
$ git reset                                       (3)

(1) 此次屬於臨時提交,所以隨便添加一個臨時註釋便可。 
(2) 此次reset刪除了WIP commit,而且把working tree設置成提交WIP快照以前的狀態。 
(3) 此時,在index中依然遺留着「snapshot WIP」提交時所作的uncommit changes,git reset將會清理index成爲還沒有提交"snapshot WIP"時的狀態便於接下來繼續工做。 

(H) Reset單獨的一個文件 
假設你已經添加了一個文件進入index,可是然後又不打算把這個文件提交,此時可使用git reset把這個文件從index中去除。 
引用
$ git reset -- frotz.c                      (1) 
$ git commit -m "Commit files in index"     (2) 
$ git add frotz.c                           (3)

(1) 把文件frotz.c從index中去除, 
(2) 把index中的文件提交 
(3) 再次把frotz.c加入index 

(I) 保留working tree並丟棄一些以前的commit 
假設你正在編輯一些文件,而且已經提交,接着繼續工做,可是如今你發現當前在working tree中的內容應該屬於另外一個branch,與這以前的commit沒有什麼關係。此時,你能夠開啓一個新的branch,而且保留着working tree中的內容。 
引用
$ git tag start 
$ git checkout -b branch1 
$ edit 
$ git commit ...                            (1) 
$ edit 
$ git checkout -b branch2                   (2) 
$ git reset --keep start                    (3)

(1) 此次是把在branch1中的改變提交了。 
(2) 此時發現,以前的提交不屬於這個branch,此時你新建了branch2,並切換到了branch2上。 
(3) 此時你能夠用reset --keep把在start以後的commit清除掉,可是保持working tree不變。 
3、高級篇
語法
git reset [-q] [<commit>] [--] <paths>… git reset (--patch | -p) [<commit>] [--] [<paths>…] git reset (--soft | --mixed | --hard | --merge | --keep) [-q] [<commit>]

DESCRIPTION

In the first and second form, copy entries from <commit> to the index. In the third form, set the current branch head (HEAD) to <commit>, optionally modifying index and working tree to match. The <commit> defaults to HEAD in all forms.安全

git reset [-q] [<commit>] [--] <paths>…

This form resets the index entries for all <paths> to their state at <commit>. (It does not affect the working tree, nor the current branch.)app

This means that git reset <paths> is the opposite of git add <paths>.ide

After running git reset <paths> to update the index entry, you can use git-checkout(1) to check the contents out of the index to the working tree. Alternatively, using git-checkout(1) and specifying a commit, you can copy the contents of a path out of a commit to the index and to the working tree in one go.ui

git reset (--patch | -p) [<commit>] [--] [<paths>…]

Interactively select hunks in the difference between the index and <commit> (defaults to HEAD). The chosen hunks are applied in reverse to the index.this

This means that git reset -p is the opposite of git add -p, i.e. you can use it to selectively reset hunks. See the 「Interactive Mode」 section of git-add(1) to learn how to operate the --patch mode.idea

git reset --<mode> [<commit>]

This form resets the current branch head to <commit> and possibly updates the index (resetting it to the tree of <commit>) and the working tree depending on <mode>, which must be one of the following:spa

--soft

Does not touch the index file nor the working tree at all (but resets the head to <commit>, just like all modes do). This leaves all your changed files "Changes to be committed", as git status would put it.

--mixed

Resets the index but not the working tree (i.e., the changed files are preserved but not marked for commit) and reports what has not been updated. This is the default action.

--hard

Resets the index and working tree. Any changes to tracked files in the working tree since <commit> are discarded.

--merge

Resets the index and updates the files in the working tree that are different between <commit> and HEAD, but keeps those which are different between the index and working tree (i.e. which have changes which have not been added). If a file that is different between <commit> and the index has unstaged changes, reset is aborted.

In other words, --merge does something like a git read-tree -u -m <commit>, but carries forward unmerged index entries.

--keep

Resets index entries and updates files in the working tree that are different between <commit> and HEAD. If a file that is different between <commit> and HEAD has local changes, reset is aborted.

If you want to undo a commit other than the latest on a branch, git-revert(1) is your friend.

OPTIONS

-q
--quiet

Be quiet, only report errors.

EXAMPLES

Undo add

$ edit <1> $ git add frotz.c filfre.c $ mailx <2> $ git reset <3> $ git pull git://info.example.com/ nitfol <4>

  1. You are happily working on something, and find the changes in these files are in good order. You do not want to see them when you run "git diff", because you plan to work on other files and changes with these files are distracting.

  2. Somebody asks you to pull, and the changes sounds worthy of merging.

  3. However, you already dirtied the index (i.e. your index does not match the HEAD commit). But you know the pull you are going to make does not affect frotz.c nor filfre.c, so you revert the index changes for these two files. Your changes in working tree remain there.

  4. Then you can pull and merge, leaving frotz.c and filfre.c changes still in the working tree.

Undo a commit and redo

$ git commit ... $ git reset --soft HEAD^ <1> $ edit <2> $ git commit -a -c ORIG_HEAD <3>

  1. This is most often done when you remembered what you just committed is incomplete, or you misspelled your commit message, or both. Leaves working tree as it was before "reset".

  2. Make corrections to working tree files.

  3. "reset" copies the old head to .git/ORIG_HEAD; redo the commit by starting with its log message. If you do not need to edit the message further, you can give -C option instead.

    See also the --amend option to git-commit(1).

Undo a commit, making it a topic branch

$ git branch topic/wip <1> $ git reset --hard HEAD~3 <2> $ git checkout topic/wip <3>

  1. You have made some commits, but realize they were premature to be in the "master" branch. You want to continue polishing them in a topic branch, so create "topic/wip" branch off of the current HEAD.

  2. Rewind the master branch to get rid of those three commits.

  3. Switch to "topic/wip" branch and keep working.

Undo commits permanently

$ git commit ... $ git reset --hard HEAD~3 <1>

  1. The last three commits (HEAD, HEAD^, and HEAD~2) were bad and you do not want to ever see them again. Do not do this if you have already given these commits to somebody else. (See the "RECOVERING FROM UPSTREAM REBASE" section in git-rebase(1) for the implications of doing so.)

Undo a merge or pull

$ git pull <1> Auto-merging nitfol CONFLICT (content): Merge conflict in nitfol Automatic merge failed; fix conflicts and then commit the result. $ git reset --hard <2> $ git pull . topic/branch <3> Updating from 41223... to 13134... Fast-forward $ git reset --hard ORIG_HEAD <4>

  1. Try to update from the upstream resulted in a lot of conflicts; you were not ready to spend a lot of time merging right now, so you decide to do that later.

  2. "pull" has not made merge commit, so "git reset --hard" which is a synonym for "git reset --hard HEAD" clears the mess from the index file and the working tree.

  3. Merge a topic branch into the current branch, which resulted in a fast-forward.

  4. But you decided that the topic branch is not ready for public consumption yet. "pull" or "merge" always leaves the original tip of the current branch in ORIG_HEAD, so resetting hard to it brings your index file and the working tree back to that state, and resets the tip of the branch to that commit.

Undo a merge or pull inside a dirty working tree

$ git pull <1> Auto-merging nitfol Merge made by recursive. nitfol | 20 +++++---- ... $ git reset --merge ORIG_HEAD <2>

  1. Even if you may have local modifications in your working tree, you can safely say "git pull" when you know that the change in the other branch does not overlap with them.

  2. After inspecting the result of the merge, you may find that the change in the other branch is unsatisfactory. Running "git reset --hard ORIG_HEAD" will let you go back to where you were, but it will discard your local changes, which you do not want. "git reset --merge" keeps your local changes.

Interrupted workflow

Suppose you are interrupted by an urgent fix request while you are in the middle of a large change. The files in your working tree are not in any shape to be committed yet, but you need to get to the other branch for a quick bugfix.

$ git checkout feature ;# you were working in "feature" branch and $ work work work ;# got interrupted $ git commit -a -m "snapshot WIP" <1> $ git checkout master $ fix fix fix $ git commit ;# commit with real log $ git checkout feature $ git reset --soft HEAD^ ;# go back to WIP state <2> $ git reset <3>

  1. This commit will get blown away so a throw-away log message is OK.

  2. This removes the WIP commit from the commit history, and sets your working tree to the state just before you made that snapshot.

  3. At this point the index file still has all the WIP changes you committed as snapshot WIP. This updates the index to show your WIP files as uncommitted.

    See also git-stash(1).

Reset a single file in the index

Suppose you have added a file to your index, but later decide you do not want to add it to your commit. You can remove the file from the index while keeping your changes with git reset.

$ git reset -- frotz.c <1> $ git commit -m "Commit files in index" <2> $ git add frotz.c <3>

  1. This removes the file from the index while keeping it in the working directory.

  2. This commits all other changes in the index.

  3. Adds the file to the index again.

Keep changes in working tree while discarding some previous commits

Suppose you are working on something and you commit it, and then you continue working a bit more, but now you think that what you have in your working tree should be in another branch that has nothing to do with what you committed previously. You can start a new branch and reset it while keeping the changes in your working tree.

$ git tag start $ git checkout -b branch1 $ edit $ git commit ... <1> $ edit $ git checkout -b branch2 <2> $ git reset --keep start <3>

  1. This commits your first edits in branch1.

  2. In the ideal world, you could have realized that the earlier commit did not belong to the new topic when you created and switched to branch2 (i.e. "git checkout -b branch2 start"), but nobody is perfect.

  3. But you can use "reset --keep" to remove the unwanted commit after you switched to "branch2".

DISCUSSION

The tables below show what happens when running:

git reset --option target

to reset the HEAD to another commit (target) with the different reset options depending on the state of the files.

In these tables, A, B, C and D are some different states of a file. For example, the first line of the first table means that if a file is in state A in the working tree, in state B in the index, in state C in HEAD and in state D in the target, then "git reset --soft target" will leave the file in the working tree in state A and in the index in state B. It resets (i.e. moves) the HEAD (i.e. the tip of the current branch, if you are on one) to "target" (which has the file in state D).

working index HEAD target working index HEAD ---------------------------------------------------- A B C D --soft A B D --mixed A D D --hard D D D --merge (disallowed) --keep (disallowed)
working index HEAD target working index HEAD ---------------------------------------------------- A B C C --soft A B C --mixed A C C --hard C C C --merge (disallowed) --keep A C C
working index HEAD target working index HEAD ---------------------------------------------------- B B C D --soft B B D --mixed B D D --hard D D D --merge D D D --keep (disallowed)
working index HEAD target working index HEAD ---------------------------------------------------- B B C C --soft B B C --mixed B C C --hard C C C --merge C C C --keep B C C
working index HEAD target working index HEAD ---------------------------------------------------- B C C D --soft B C D --mixed B D D --hard D D D --merge (disallowed) --keep (disallowed)
working index HEAD target working index HEAD ---------------------------------------------------- B C C C --soft B C C --mixed B C C --hard C C C --merge B C C --keep B C C

"reset --merge" is meant to be used when resetting out of a conflicted merge. Any mergy operation guarantees that the working tree file that is involved in the merge does not have local change wrt the index before it starts, and that it writes the result out to the working tree. So if we see some difference between the index and the target and also between the index and the working tree, then it means that we are not resetting out from a state that a mergy operation left after failing with a conflict. That is why we disallow --merge option in this case.

"reset --keep" is meant to be used when removing some of the last commits in the current branch while keeping changes in the working tree. If there could be conflicts between the changes in the commit we want to remove and the changes in the working tree we want to keep, the reset is disallowed. That’s why it is disallowed if there are both changes between the working tree and HEAD, and between HEAD and the target. To be safe, it is also disallowed when there are unmerged entries.

The following tables show what happens when there are unmerged entries:

working index HEAD target working index HEAD ---------------------------------------------------- X U A B --soft (disallowed) --mixed X B B --hard B B B --merge B B B --keep (disallowed)
working index HEAD target working index HEAD ---------------------------------------------------- X U A A --soft (disallowed) --mixed X A A --hard A A A --merge A A A --keep (disallowed)

X means any state and U means an unmerged index.

結束

相關文章
相關標籤/搜索