“Git:基本操作”的版本间差异

来自Wikioe
跳到导航 跳到搜索
第550行: 第550行:


=== reset 与 revert ===
=== reset 与 revert ===
reset 与 revert 都是进行“撤销”操作,但其效果并不相同:
{| class="wikitable"
|-
! reset !! revert
|-
|
* 直接删除指定的commit。(就像从没有发生过一样,即'''撤销了操作''')
|
* 提交新版本,将需要revert的版本内容反向修改回去。(提交新的commit来“纠正”修改,即'''撤销了修改''')
# 之前的commit和history都会保留;
# 版本会递增,不影响之前提交的内容;
|-
| colspan="2"|对于分支merge的撤销操作(如将“eijux”合并到“master”):
# reset:
#* reset之后与merge之前一样,存在两个分支,并且没有merge的操作历史;
#revert:
#* revert之后,分支合并状态不变(“eijux”已被合并),但“master”的内容与merge之前一致,并有merge操作的历史;
#*:(即只是撤销了“eijux”合并到“master”所修改的“master内容”,merge操作仍然有效b)
|}

2020年9月21日 (一) 15:14的版本


常用操作

Git基本操作.jpg

仓库

命令 说明 备注
git init 初始化Git仓库
  1. git init:在当前目录初始化Git仓库
  2. git init <newrepo>:在指定目录(<newrepo>)初始化Git仓库
git clone 克隆Git仓库
  1. git clone <repo>:克隆仓库(<repo>)到当前目录
  2. git clone -o<远程主机名> <repo>:克隆仓库(<repo>)当前目录,并为远程主机设置主机名
  3. git clone <repo> <directory>:克隆仓库(<repo>)到指定目录(<directory>

修改

命令 说明 备注
git add 将工作区新增或修改的文件添加到暂存区
  1. git add [file1] [file2]:添加一个或多个文件到暂存区;
  2. git add [dir]:添加指定目录到暂存区(包括子目录);
  3. git add .:添加当前目录下的所有文件到暂存区;
  4. git add -f [file]:强制添加存在于.gitignore的文件;
git commit 提交暂存区到本地仓库
  1. git commit -m [message]:提交暂存区到本地仓库;
  2. git commit [file1] [file2] -m [message]:提交暂存区的指定文件;
  3. git commit ([file1] [file2]) -am [message]:直接提交(跳过git add);
git status 查看仓库当前的状态,显示有变更的文件
Microsoft Windows [版本 10.0.19041.508]
(c) 2019 Microsoft Corporation保留所有权利

D:\git\eijux>git status
On branch master
Your branch is up to date with 'eijux/master'.

Changed not staged for commit:
  (use "git add <file>..." to include in what will be committed)
  (use "git restore --staged <file>..." to unstage)
        new file:   1.txt

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   2.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        3.txt
  1. Changed not staged for commit:修改文件,未add
  2. Changes to be committed:修改并add,未commit
  3. Untracked files:新增文件,从未add
git diff 比较文件的不同,即暂存区和工作区的差异
  1. git diff ([file]):查看暂存区和工作区的改动;
  2. git diff --cached ([file]):(或git diff --staged ([file]))暂存区和上一个commit的差异;
  3. git diff HEAD ([file]):查看工作区与当前分支最新commit之间的差异;
  4. git diff [branchName] [file]:当前分支的文件与[branchName]分支的文件进行比较;
  5. git diff [commitId] [file]:当前分支的文件与[commitId]提交的文件进行比较;
  6. git diff --stat:显示摘要而非整个diff;
  7. git diff [first-branch]...[second-branch]:查看两次提交之间的差异;
git stash 保存工作现场
  1. git stash list:查看所有保存的工作现场;
  2. git stash apply <stash>:恢复stash(但不删除);
  3. git stash drop <stash>:删除stash;
  4. git stash pop <stash>:恢复并删除stash;
  5. <stash>形如“stash@{0}”,可在list中看到:
    • e.g. git stash pop stash@{0}
git reset 回退版本

git reset [--soft | --mixed | --hard] [HEAD]
关于参数:

  1. --soft:将HEAD指向指定commit(这使得所有更改的文件都“等待提交”);
  2. --mixed:(默认)重置索引,但不重置工作树(保留更改的文件,标记为:未提交?未add?);
  3. --hard:重置索引和工作树(自<commit>以来对工作树中跟踪文件的任何更改都将被丢弃);
  4. --merge:重置索引,并
    1. 1、在工作树中更新‘<commit>与HEAD不同’的文件,
    2. 2、在工作树中保留‘索引与工作树不同’的文件(即,未add的更改)。
    3. 如果‘<commit>和索引不同’的文件具有未暂存的更改,则会中止重置。
  5. --keep:重置索引,并
    1. 1、在工作树中更新‘<commit>与HEAD不同’的文件。
    2. 如果‘<commit>与HEAD不同’的文件具有本地修改,则会中止重置。

关于[HEAD]:

  1. 当前版本:HEAD^HEAD~0
  2. 上一个版本:HEAD^HEAD~1
  3. 上上一个版本:HEAD^^HEAD^2
  4. 上上上一个版本:HEAD^^^HEAD^3
    以此类推
git revert 撤销操作
  1. revert是将需要的版本内容反向修改回去,并提交一个新版本;
  2. git revert HEAD:撤销前一次 commit;
  3. git revert HEAD^:撤销前前一次 commit;
  4. git revert <commitID>:撤销指定的版本;
git mv 移动或重命名工作区文件
  1. git mv -f [file] [newfile]:强制执行;
git rm 删除工作区文件
  1. git rm <file>:将文件从暂存区和工作区中删除;
  2. git rm --cached <file>:将文件从暂存删除,工作区保留(即停止追踪指定文件,通常是在文件曾经被git管理过,现在不需要被git接管的时候使用);
  3. git rm -r *:递归删除;
  4. git rm -f <file>:强制删除;

关于撤销工作区修改

  1. 若工作区修改未add到stage:
    git checkout -- <file>
  2. 若工作区修改已add,但未commit:
    1. git reset HEAD <file>(1、从版本库commit中恢复到stage)
    2. git checkout -- <file>(2、撤销工作区修改)

分支

命令 说明 备注
git branch 分支命令
  1. git branch:查看所有本地分支
  2. git branch -r:查看所有远程分支
  3. git branch -a:查看本地和远程的所有分支
  4. git branch <branchname>:创建<branchname>分支
  5. git branch -d <branchname>:删除<branchname>分支
  6. git branch -D <branchname>:强制删除一个未被合并过的分支<branchname>
  7. git branch --set-upstream <本地分支名> <远程主机名>/<远程分支名>:手动在本地分支与远程分支间建立追踪关系;
git checkout 分支切换
  1. git checkout <branchname>:切换到<branchname>分支
  2. git checkout -b <branchname>创建并切换到<branchname>分支
  3. git checkout -b <本地新建分支> <远程主机名>/<远程分支>:在远程分支基础上新建本地分支(先有远程主机信息git fetch <远程主机名> <远程分支>
  4. git checkout -- <fileName>:“--”表示撤销工作区文件<fileName>的修改
    1. 工作区修改,但未add到暂存区:checkout重置到与版本库一致
    2. 工作区修改并add后,再次修改:checkout重置到与暂存区一致
切换分支的时候,Git会用该分支最后提交的快照替换本地工作目录的内容。
git merge 分支合并

Note1: Git merge.png

  1. 合并分支并产生新的提交
  2. 不会改变当前分支的开始位置;
  3. 只处理一次冲突;
  4. 引入了一次合并的历史记录,合并后的所有 commit 会按照提交时间从旧到新排列;
  5. 合并后分支过程信息更多更复杂,可能会提高之后查找问题的难度;

Note2:

  1. git merge <branch1>:将<branch1>分支合并到当前分支;
    branchB和并到branchA:切换到branchA中执行“git merge branchB”。
  2. git merge <branch1> <branch2> <branch3> <branch4>:合并多个分支到当前分支;
git rebase 分支(变基)合并

Note1: Git rebase.png

  1. (变基)合并分支但并不产生新的提交
  2. 改变了当前分支的开始位置
  3. 可能会有多次冲突处理
  4. 没有多余的合并历史的记录,且合并后的 commit 顺序不一定按照 commit 的提交时间排列
  5. 合并后分支上每个 commit 点都是相对独立完整的功能单元;

Note2:

  1. git rebase <branch1>
  2. git rebase --continue:(解决完冲突并git add后)继续应用(apply)余下的补丁进行合并;
  3. git rebase --abort:终止rebase的行动,并将分支回退到rebase开始前的状态;

冲突合并:
冲突合并涉及文件添加、移除的操作,还包括文件修改内容的合并。

远程

命令 说明 备注
git remote 管理远程仓库主机名
  1. git remote -v:显示所有远程仓库
  2. git remote show [remoteName]:显示远程仓库的信息
    如:git remote show origin
  3. git remote add [remoteName] [url]:添加关联远程版本库
    如:git remote add origin git@github.com:tianqixin/runoob-git-test.git
  4. git remote rm [remoteName]:删除远程仓库
  5. git remote rename [old_name] [new_name]:修改远程仓库名
git pull

下载远程代码并合并:
git pull <远程主机名> <远程分支名>:<本地分支名>

Note1:

  1. 合并到当前分支,可省略“:<本地分支名>”;
  2. 等效于git fetch+git merge FETCH_HEAD(概念上等效,实现上不一样???)。

Note2:

  1. git pull --rebase <远程主机名> <远程分支名>:<本地分支名>:合并需要采用rebase模式的pull;
  2. git pull -p:若服务器删除了某分支,拉取远程分支的时候,同步删除本地的该分支;
    等同于:git fetch --prune <远程主机名>git fetch -p

Note3:

  1. git pull <远程主机名> <远程分支名>
    拉取远程主机的分支,并与本地当前分支合并;
  2. git pull <远程主机名>
    拉取远程主机的对应分支,并与本地当前分支合并(当前分支与远程分支必须存在追踪关系);
  3. git pull
    拉取远程主机的对应分支,并与本地当前分支合并(当前分支只有一个追踪分支);
git push

上传远程代码并合并:
git push <远程主机名> <本地分支名>:<远程分支名>

Note1:

  1. git push --delete:删除远程主机的分支;
  2. git push --force:(慎用)强制推送,忽略远程分支版本比本地高(应先pull);
  3. git push --all:无论对应远程分支是否存在,将本地的所有分支都推送到远程主机;
  4. git push --tags:推送所有未推送过的本地标签;
  5. git push <tag>:推送本地标签<tag>;

Note2:

  1. git push -u <远程主机名> <本地分支名>
    如果当前分支与多个主机存在追踪关系,则可以使用-u参数指定一个默认主机,这样后面就可以不加任何参数使用git push;
  2. git push <远程主机名> <本地分支名>
    表示将本地分支推送到与之存在追踪关系的远程分支(通常两者同名),如果该远程分支不存在,则会被新建;
  3. git push <远程主机名> :<远程分支名>
    表示删除对应的远程分支(推送空的本地分支到远程);
  4. git push <远程主机名>
    表示将当前分支推送到远程主机的对应分支(当前分支与远程分支必须存在追踪关系);
  5. git push
    表示将当前分支推送到远程主机的对应分支(当前分支只有一个追踪分支);

Note3:关于推送方式

  1. simple方式:不带任何参数的git push,默认只推送当前分支;(默认)
  2. matching方式:推送所有有对应的远程分支的本地分支;(Git 2.0之前默认)
  3. git config中的push.default可配置推送方式;

git fetch

获取远程主机的版本库更新(commit):git fetch [alias] [branch]

Download objects and refs from another repository

Note1:

  1. FETCH_HEAD:是一个版本链接,记录在本地的文件中(.git/FETCH_HEAD),指向着目前已经从远程仓库取下来的分支的末端版本;
  2. “git fetch”将:
    1. 更新“git remote”中所有远程仓库所包含分支的最新commit-id, 将其记录到“.git/FETCH_HEAD”文件中;
    2. 下载内容到“.git\objects”及“.git\refs”;
  3. git fetchgit pull
    1. git fetch拉取更新信息(commitID),但不会合并到本地工作目录;
    2. git pull将本地库更新至远程库的最新状态;

Note2:

  1. git fetch <远程主机名> <远程分支1> <远程分支2> <远程分支3>:从远程仓库拉取多个分支;
  2. git fetch <远程主机名> <远程分支>:拉取指定主机指定分支的更新;
  3. git fetch <远程主机名>:拉取指定主机的更新(不指定分支时通常默认为master);
  4. git fetch :拉取所有分支(branch)的更新;

Note3:

  1. 拉取分支更新,并在本地新建分支:
    • 不切换到该分支:
    1. git fetch <远程主机名> <远程分支>:<本地新建分支>
    • 并切换到该分支:
    1. git fetch <远程主机名> <远程分支>
    2. git checkout -b <本地新建分支> <远程主机名>/<远程分支>
  2. 拉取远程分支更新,并合并到本地分支(与git merge配合使用):
    1. git fetch origin master:从origin拉取分支master的更新;
    2. git merge origin/master:合并origin/master分支到当前分支
  3. 拉取多分支更新:
    1. 拉取多个分支:git fetch origin master stable oldstable,从远程仓库origin拉取master、stable、oldstable分支的更新;
    2. 合并多个分支:git merge origin/master hotfix-2275 hotfix-2276 hotfix-2290,合并origin/master、origin/stable、origin/oldstable到当前分支。

日志

命令 说明 示例
git log 查看版本提交历史
Microsoft Windows [版本 10.0.19041.508]
(c) 2019 Microsoft Corporation保留所有权利

D:\git\eijux>git log
commit 5e26159ad738b08b9321eba9ecaeff39c8acc42f (HEAD -> master, 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
(END)

Note:

  1. git log --graph:查看分支commit关联图
git reflog 查看版本命令历史
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

标签

tag实际就是一个指向commit的指针,将一个有意义的tag名称与commit相关联。
git标签有两种类型:

  1. 轻量级的(lightweight):实际上它就是个指向特定提交对象的引用。
  2. 含附注的(annotated):
    实际上是存储在仓库中的一个独立对象,有自身的校验和信息,包含标签名称,电子邮件地址和日期,及标签说明,标签本身也允许使用 GNU Privacy Guard (GPG) 来签署或验证。
命令 说明 备注
git tag git标签

Note:

  1. git tag:查看所有标签;
  2. git tag <tagname>:创建标签(默认指定到当前分支);
  3. git tag <tagname> <commitID>:创建标签,并关联到指定分支;
  4. git tag -a <tagname>:创建带注解的标签(Git会打开编辑器来编辑tag的注解信息);
  5. git tag -d <tagname>:删除标签;
  6. git show <tagname>:查看tag关联的commit信息
  7. git tag -a <tagname> -m "标签信息":指定标签信息
  8. git tag -s <tagname> -m "标签信息":PGP签名标签

如:

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

D:\git\eijux>git tag

D:\git\eijux>git tag rc1.0

D:\git\eijux>git tag -a rc2.0

# Write a message for tag:
#   rc2.0
# Lines starting with '#' will be ignored.

D:\git\eijux>git tag rc3.0 ffa1

D:\git\eijux>git log --decorate
commit 5e26159ad738b08b9321eba9ecaeff39c8acc42f (HEAD -> master, tag: rc2.0, 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 (tag: rc3.0)
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

D:\git\eijux>git show rc3.0
commit ffa14211ab7088d9782de019328238dcfe09a4bc (tag: rc3.0)
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

D:\git\eijux>

命令辨析

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 pullgit fetch后跟git merge FETCH_HEAD的缩写。
更准确地说,git pull使用给定的参数运行git fetch,并调用git merge将检索到的分支头合并到当前分支中。 使用--rebase,它运行git rebase而不是git merge

fetch理解:

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

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---------┘

关于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开始前状态。

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

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

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