Git:命令辨析

来自Wikioe
跳到导航 跳到搜索


pull 与 fetch

More precisely, git pull runs git fetch with the given parameters and calls git merge to merge the retrieved branch heads into the current branch. With --rebase, it runs git rebase instead of git merge.

更准确地说,git pull 使用给定的参数运行 git fetch ,并调用 git merge 将检索到的分支头合并到当前分支中。 带有 --rebase 时,它运行 git rebase 而不是 git merge。


即,在默认模式下,git pullgit fetch 后跟 git merge FETCH_HEAD 的缩写。

fetch 理解

  1. fetch 是否有下载?
    当然有下载,拉取的更新内容保存在“.git\objects”及“.git\refs”中,只是没有合并到当前的工作区,所以没有直观的改变;毕竟merge是不会有下载操作的。
  2. fetch 更新的是什么内容?
    1. fetch 会下载远程分支 orign/master 的文件存储对象到 .git\objects
    2. fetch 会更新 FETCH_HEAD 文件(如:5e26159ad738b08b9321eba9ecaeff39c8acc42f branch 'master' of github.com:Eijux/eijux),其中commit_ID对应 .git\objects 的对象;
  3. 本地文件与分支的关系?
    .git\refs\head\          [本地分支]
    .git\refs\remotes\     [正在跟踪的分支]
    

使用

  1. 当本地没有新的修改时:
    1. 使用 fetch 获取远程分支;
    2. 重新 checkout 该分支。【???或许是这样】
  2. 当本地有新修改(未 commit 的修改、未 push 的 commit)时:
    1. 必须保证工作区没有修改;
      可将修改的内容:1、提交为新的 commit;2、保存到 stage;
    2. 使用 pull 拉取远程分支并合并(merge 或 rebase);
    3. 解决合并冲突(如果有),并在本地提交;
    4. 通过 push 推送本地分支到远程。

merge 与 rebase

merge 与 rebase 同为合并分支,但是还是有区别的:

merge rebase
Git merge.png Git rebase.png
产生新的提交 没有新提交,但会合并之前的commit历史
不改变当前分支的开始位置 改变了当前分支的开始位置
只处理一次冲突 可能会有多次冲突处理
合并后的所有 commit 会按照提交时间从旧到新排列 合并后的 commit 顺序不一定按照 commit 的提交时间排列
合并后分支过程信息更多更复杂 合并后分支上每个 commit 点都是相对独立完整的功能单元

执行过程:merge

merge的过程
Git merge合并过程.jpg 以左图为例,
git merge origin”将两个分支的最新commit的内容进行合并,进行冲突处理之后作为新的commit提交到“mywork”分支:
git logg查看commit时,其顺序为:(有时间顺序)
          C1-----C2-----C3-----C4-----C7
         └-------C5-----C6---------┘

什么是“Fast-forward”?

Fast-forward,即“快速合并”:

    由于你想要合并的分支 hotfix 所指向的提交 C4 是你所在的提交 C2 的直接后继, 因此 Git 会直接将指针向前移动。——换句话说,当你试图合并两个分支时, 如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候,只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)”。

示例:

  1. Fast-forward:
    Git:merge e.g. ff 1.png <<<:前 | 后:>>> Git:merge e.g. ff 2.png
  2. 非 Fast-forward:
    Git:merge e.g. not-ff 1.png <<<:前 | 后:>>> Git:merge e.g. not-ff 2.png

执行过程:rebase

rebase的过程
Git rebase合并过程.jpg

左图:

  1. git rebase origin”会把“mywork”分支里的所有提交(commit)取消掉,并临时保存为补丁(位于“.git/rebase”);
  2. 然后把“mywork”分支更新为到“origin”分支末尾,最后把保存的这些补丁应用到“mywork”分支上。
(老的commit会被丢弃,并在git gc发生时被删除?)(参见:图解git rebase 与merge的区别
git logg查看commit时,其顺序为:(C5、C6未按时间顺序)
          C1-----C2-----C3-----C4-----C5-----C6

关于rebase的冲突

rebase过程中可能出现多次冲突,Git会停止rebase来进行冲突解决;

  1. 在解决冲突后,用" git add "命令去更新这些内容的索引(无需执行git commit);
  2. 执行“git rebase --continue”继续rebase过程;
在rebase过程的任何时候,都可以用“git rebase --abort”来终止rebase,并且将分支回退到rebase开始前状态。

使用

对于:
git checkout branchA
git merge branchB   是将 B 合并到 A
git rebase branchB   是将 A 变基到 B(将A中的commit重新提交到B???但是IDEA文档中说的好像相反)

示例:

  1. merge:将 hotfix 合并到 master
    $ git checkout master
    $ git merge hotfix
    
    Git:merge e.g. not-ff 1.png <<<:前 | 后:>>> Git:merge e.g. not-ff 2.png
  2. rebase:将 experiment 变基到 master
    $ git checkout experiment
    $ git rebase master
    
    Git:rebase e.g. 1.png <<<:前 | 后:>>> Git:rebase e.g. 2.png

The Golden Rule of Rebasing rebase

never use it on public branches(不要在公共分支上使用)
rebase的过程
Golden rule in using rebase.png

如左图,执行git checkout featuregit rebase master,rebase将master所有commit移动到feature之后:

  1. 分支历史与其他master分支使用者的产生分歧;
  2. master的分支历史追踪变得极为困难,不利于问题排查;

所以,在公共分支、需要保留历史的分支,不应该使用rebase!

reset、revert与checkout

Commit-level File-level
checkout
Checkout Commit.png
  • git checkout <commit/HEAD^?>
    移动HEAD到另外一个分支(或commit),并更新工作目录。
Checkout file.png
  • git checkout <commit/HEAD^?> <file>
    将worktree文件<file>重置到指定commit的版本(将丢弃工作区文件<file>未add的修改)。
reset
Reset Commit.png
  • git reset [--soft/--mixed/--hard] <commit/HEAD^?>
    将分支的指向其他commit,以进行版本回退(被跳过的commit成了dangling commit,将在下次git垃圾回收被删除)。
Reset File.png
  • git reset <commit/HEAD^?> <file>
    将stage文件<file>重置到指定commit的版本。
  • [--soft/--mixed/--hard]对此不起作用,stage总会被更新, worktree不会被更新)
revert
Revert Commit.png
  • git reset <commit/HEAD^?>
    通过新建commit进行反向修改,以实现撤销某个commit的修改。

撤销commit:reset 与 revert

reset 与 revert 都是进行“撤销”操作,但其效果并不相同:

reset revert
  • 直接删除指定的commit。(就像从没有发生过一样,即撤销了操作
  • 提交新版本,将需要revert的版本内容反向修改回去。(提交新的commit来“纠正”修改,即撤销了修改
  1. 之前的commit和history都会保留;
  2. 版本会递增,不影响之前提交的内容;
对于分支merge的撤销操作(如将“eijux”合并到“master”):
  1. reset:
    • reset之后与merge之前一样,两个分支未被合并,且没有merge的操作历史;
  2. revert:
    • revert之后,分支合并状态不变,并有merge操作的历史,但“master”的内容与merge之前一致,;
      (即只是撤销了“eijux”合并到“master”所修改的“master内容”,merge操作仍然有效)

log 与 reflog

log reflog

显示所有提交过的版本信息

查看所有分支的所有操作记录

对于已经被删除的commit记录和reset的操作:git log则没有相关的commit信息,git reflog则可以看到与commit相关的操作记录;

Microsoft Windows [版本 10.0.19041.508]
(c) 2019 Microsoft Corporation保留所有权利

D:\git\eijux>git log
commit 5e26159ad738b08b9321eba9ecaeff39c8acc42f (HEAD -> master, tag: rc1.0, eijux/master)
Author: Eijux <chen@eijux.com>
Date:   Fri Nov 1 01:18:47 2019 +0800

    idea commit test 01<E7><82><B9>18<E5><88><86>

commit ffa14211ab7088d9782de019328238dcfe09a4bc
Merge: 3fbc79a fc32a4a
Author: Eijux <chen@eijux.com>
Date:   Thu Oct 31 16:03:24 2019 +0800

    Merge branch 'master' of github.com:Eijux/eijux

commit 3fbc79ae2c8b7199c22133d1f3e4115848dbe749
Author: Eijux <chen@eijux.com>
Date:   Thu Oct 31 04:19:09 2019 +0800

    idea commit test

commit fc32a4a8e24b963097f088a975d05ffa68f0c0bb
Author: Eijux <chen@eijux.com>
Date:   Wed Oct 30 05:13:47 2019 +0800

    add gitignore file

...
(END)
Microsoft Windows [版本 10.0.19041.508]
(c) 2019 Microsoft Corporation保留所有权利

D:\git\eijux>git reflog
5e26159 (HEAD -> master, eijux/master) HEAD@{0}: pull eijux master: Fast-forward
fc32a4a HEAD@{1}: commit: add gitignore file
d07be0a HEAD@{2}: commit: branch management test
492be10 HEAD@{3}: merge dev: Merge made by the 'recursive' strategy.
fc1038b HEAD@{4}: checkout: moving from dev to master
9d0f76e (dev) HEAD@{5}: commit: write a line on branch dev
7640602 (eijux/dev) HEAD@{6}: checkout: moving from master to dev
fc1038b HEAD@{7}: reset: moving to HEAD
fc1038b HEAD@{8}: commit: branch back to master
7640602 (eijux/dev) HEAD@{9}: merge dev: Fast-forward
63fa5ac HEAD@{10}: checkout: moving from dev to master
7640602 (eijux/dev) HEAD@{11}: commit: add dev branch
63fa5ac HEAD@{12}: checkout: moving from master to dev
63fa5ac HEAD@{13}: pull eijux master --allow-unrelated-histories: Merge made by the 'recursive' strategy.
da60d32 HEAD@{14}: commit: add t5.txt
e88cb69 HEAD@{15}: commit: delete rmtest.txt
cc1e40c HEAD@{16}: commit: changed t4 and add t5
1707196 HEAD@{17}: commit: changed t4
378fb5a HEAD@{18}: commit: add t4.txt
e3501ab HEAD@{19}: commit: add t4
ea463d0 HEAD@{20}: commit: add t2.txt
92ad079 HEAD@{21}: commit: git commit single file test
061529e HEAD@{22}: reset: moving to 061529
69e722f HEAD@{23}: reset: moving to head
69e722f HEAD@{24}: reset: moving to head^
061529e HEAD@{25}: reset: moving to 061529
69e722f HEAD@{26}: reset: moving to 69e722
061529e HEAD@{27}: commit: git diff test
69e722f HEAD@{28}: commit: banben test
fd1fd5f HEAD@{29}: commit (initial): git add test