前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Git汇总--版本库操作

Git汇总--版本库操作

作者头像
奋飛
发布2021-08-31 17:03:47
4090
发布2021-08-31 17:03:47
举报
文章被收录于专栏:Super 前端

接上篇 Git汇总–对象及版本库存储

版本库操作

日志–log

显示提交历史! 当不使用任何参数调用,相当于使用了缺省的参数HEAD,即显示当前HEAD能够访问到的所有历史提交。可以指定某个远程或者分支进行查看:

代码语言:javascript
复制
$ git log upstream/master

参数

说明

--oneline

最精简的日志输出

--graph

分支图显示

-

显示最近的几条日志

--stat

显示每次提交的变更概要

--all

显示所有分支的历史记录

提交–commit

commit 分为两种:一种是常规的 commit,也就是使用 git commit 提交的 commit;另一种是 merge commit,在使用 git merge 合并两个分支之后,你将会得到一个新的 merge commit。

merge commit 和普通 commit 的不同之处在于 merge commit 包含两个 parent commit,代表该 merge commit 是从哪两个 commit 合并过来的。

代码语言:javascript
复制
# cdc4a1为merge生成的commitID
$ git show cdc4a1
commit cdc4a1c252171c24b7c98b92a6fb94010637de44 (HEAD -> hotfix-inspection, origin/hotfix-inspection)
Merge: 81cf95f5 41cc17e6

使用命令git describe 将提交显示为一个易记的名称。

  • 这个易记的名称来自于建立在该提交上的里程碑;
  • 如果提交没有对应的里程碑,但是在其祖先版本上建有里程碑,则使用类似--g(“基础版本号” - 距离“基础版本”的数字 - 该提交的SHA1哈希值缩写)的格式显示;
  • 如果提交本身没有包含里程碑,其祖先版本上也没有里程碑,可以通过传递--always参数显示精简提交ID,否则出错。
代码语言:javascript
复制
$ git describe
v2.0.0-143-gcffed5c2

补充:最后一次的提交信息,会存储在**.git/COMMIT_EDITMSG** 中,这对于对提交信息格式校验很有帮助, 具体可以查看:Git提交信息规范化

代码语言:javascript
复制
$ cat .git/COMMIT_EDITMSG
feat(git): git总结

可以在commit命令后加参数-s,为在提交说明的最后添加“Signed-off-by:”签名。

代码语言:javascript
复制
$ git commit -s -m "提交说明"

没有对工作区的文件进行任何修改,Git默认不会执行提交,参数--allow-empty 允许执行空白提交。

代码语言:javascript
复制
# 重新修改最新的提交,改正作者和提交者的错误信息
$ git commit --amend --allow-empty --reset-author

提交空文件夹: 默认情况下,Git不能对空文件夹进行提交。所以,我们可以在相关文件夹下创建一个文件.gitkeep来进行占位。

建议:

  • 一次提交只干一件事
  • 每次提交尽量完整,可以使用git stash或其他分支保持当前进度
  • 尽量保持暂存区和HEAD一致(即add后的内容,及时commit)
对比变更

上述提到的工作区、暂存区、HEAD,如何做比较呢?

命令

说明

git diff

工作区和暂存区比较

git diff --cached/--staged

暂存区和HEAD(master)比较

git diff HEAD/master

工作区和HEAD(当前工作分支,注意非远程)

获取–fetch or pull

fetch

从另一个存储库下载对象和引用。

在执行git fetch命令的时候,可以通过 --no-tags 参数设置不获取里程碑只获取分支及提交

代码语言:javascript
复制
$ git fetch --no-tags

或在注册远程版本库的时候,使用--no-tags 参数避免将远程版本库的里程碑引入本地版本库

代码语言:javascript
复制
$ git remote add --no-tags

获取的引用名称及其指向的对象名称将写入.git / FETCH_HEAD 中。

代码语言:javascript
复制
$ cat .git/FETCH_HEAD
7ccd4971011fe8b0df6599efb38d011f25975979 branch 'master' of x.x.x.x:namespace/project-name

git fetch origin 就相当于执行了下面的命令,将远程版本库的所有分支复制为本地的远程分支

代码语言:javascript
复制
$ git fetch origin +refs/heads/*:refs/remotes/origin/*

示例:合并upstream/master提交到本地

获取到的提交会更新到本地跟踪共享版本库(远程)master分支的本地引用.git/refs/remotes/upstream/master

代码语言:javascript
复制
$ git fetch upstream master
# 合并操作
$ git merge upstream/master
pull

git pull 又都做了哪些操作?

代码语言:javascript
复制
$ git config --list
remote.origin.url=git@x.x.x.x:project-namespace/project-name.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
branch.master.remote=origin
branch.master.merge=refs/heads/master
# 另外一个分支
branch.feature-v2.1.remote=origin
branch.feature-v2.1.merge=refs/heads/feature-v2.1
# 另外一个远程
remote.upstream.url=git@y.y.y.y:project-namespace/project-name.git
remote.upstream.fetch=+refs/heads/*:refs/remotes/upstream/*
代码语言:javascript
复制
$ git pull
  • 不带参数执行git pull 相当于执行了git pull
  • 当前分支未设置 branch..remote,则为origin
  • 获取的远程版本库的URL地址由 remote..url给出
  • 如果为注册的远程版本库设置了fetch参数,即通过 remote..fetch 配置了一个引用表达式,则使用该引用表达式执行获取操作
  • 合并的分支,如果设定了branch..merge,则对其设定的分支执行合并,否则报错退出

所以,上述fetch示例可以通过下述命令替代

代码语言:javascript
复制
$ git pull upstream master

推送–push

代码语言:javascript
复制
$ git push <remote> <branchname>
  • 不带参数执行git push相当于执行了git push
  • 当前分支未设置 branch..remote,则为origin
  • 如果为注册的远程版本库设置了push参数,即通过remote..push配置了一个引用表达式,则使用该引用表达式执行推送
  • 否则使用“:”作为引用表达式。该表达式的含义是同名分支推送,即对所有在远程版本库有同名分支的本地分支执行推送。
代码语言:javascript
复制
$ git push branch.<branchname>.remote branchname

个人建议: push默认的规则使得git push 可以像 git push origin 一样的效果,但是个人并不建议这种操作(你要明确你的确是想向origin提交;然后你的本地分支和远程分支名称必须一致<当然强烈建议创建分支本地和远程保持一致!>),以上,还是希望提供完整的命令!

重置–reset or reflog or revert

.git/refs/heads/ 中记录了其分支中对应的最新提交ID,下述为master分支最新提交ID:

代码语言:javascript
复制
$ cat .git/refs/heads/master
e695606fc5e31b2ff9038a48a3d363f4c21a3d86
reset

重置命令git reset 的一个用途就是修改引用(如master)的游标。可以将“游标”指向任意一个存在的提交ID。

代码语言:javascript
复制
$ cat .git/HEAD
ref: refs/heads/master

在执行重置命令的时候没有使用任何参数对所要重置的分支名进行设置,这是因为重置命名实际上所针对的是头指针HEAD。

方式一:不会重置引用
代码语言:javascript
复制
$ git reset [-q] [<commit>] [--] <paths>...

包含了路径的用法。不会重置引用,更不会改变工作区,而是用指定提交状态()下的文件()替换掉暂存区中的文件。

代码语言:javascript
复制
$ git reset HEAD/commitID [--] <filePaths>

注意: 为了避免路径和引用(或者提交ID)同名而冲突,可以在前用两个连续的短线作为分隔。

方式二:重置引用
代码语言:javascript
复制
$ git reset [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]

不使用路径的用法。会重置引用。根据不同的选项,可以对暂存区或者工作区进行重置。会彻底的丢弃历史(**git log**查看不到提及历史)。

参数

影响范围

–hard

引用指向新的提交ID,替换暂存区和工作区

–soft

只更改引用的指向,不改变暂存区和工作区

–mixed(缺省即为--mixed)

更改引用的指向以及重置暂存区,但是不改变工作区

代码语言:javascript
复制
$ git reset --hard HEAD^
$ git reset --hard <commitID>

!!!注意: 使用重置命令很危险,会彻底的丢弃历史。那么还能够通过浏览提交历史的办法找到丢弃的提交ID,再使用重置命令恢复历史么?不可能!因为重置让提交历史也改变了。在一个共享的仓库中这会造成问题的。如果其他人已经有你将要重写的提交,你应当避免使用 reset;如果有任何其他提交在合并之后创建了,那么这个方法也会无效;移动引用实际上会丢失那些改动。

reflog

显示操作历史!

上述reset的第二种方式会丢失历史,如果真的做了上述操作,该如何还原呢?

代码语言:javascript
复制
$ ll .git/logs
-rw-r--r--  1 ligang  staff   111K 11 30 15:02 HEAD
drwxr-xr-x  5 ligang  staff   160B 11 29 09:44 refs

通过.git/logs 目录下日志文件记录了分支的变更。默认非裸版本库(带有工作区)都提供分支日志功能,这是因为带有工作区的版本库都有如下设置。

代码语言:javascript
复制
$ git config core.logallrefupdates

Git提供了一个**git reflog** 命令,对这个文件进行操作。使用show子命令可以显示此文件的内容。

代码语言:javascript
复制
$ git reflog show HEAD
61f26bdb (HEAD -> master) HEAD@{0}: commit: camile.txt
622c9bd0 HEAD@{1}: reset: moving to HEAD^
4e0e3777 HEAD@{2}: commit: camile.txt
622c9bd0 HEAD@{3}: reset: moving to HEAD^

注意: 使用git reflog 的输出和直接查看日志文件最大的不同在于显示顺序的不同,即最新改变放在了最前面显示,而且只显示每次改变的最终的SHA1哈希值

代码语言:javascript
复制
# 还原某个提交!!
$ git reset --hard HEAD@{2}

git loggit reflog 区别:

  • git log 显示提交历史;
  • git reflog 显示操作历史(包括已经被删除的 commit 记录和 reset 的操作)。
revert

revert 命令十分直观易用,相当于做一次被 revert 的提交的「反操作」并形成一个新的 commit。

上面commit中提到了,commit分为常规commit和merge的commit两种。所以,revert也对应两种方式:

常规commit

使用 git revert 即可,git 会生成一个新的 commit,将指定的 commit 内容从当前分支上撤除

merge的commit

revert merge commit 这时需要添加 -m 选项以代表这次 revert 的是一个 merge commit,-m 选项接收的参数是一个数字,数字取值为 1 和 2,也就是 Merge 行里面列出来的第一个还是第二个。

代码语言:javascript
复制
$ git log
commit e6930af365f89b2bdc47d4e190b0e7e1c10bc0b8
Merge: e215da30 fb5baba1
# 具有merge标识,是从e215da30和fb5baba1两个commit合并而来
$ git revert -m 1 e6930af365f89b2bdc47d4e190b0e7e1c10bc0b8

注意: revert后的代码想在合并进来,必须进行反向revert,否则会丢掉相关内容!http://blog.psjay.com/posts/git-revert-merge-commit/

检出–checkout

git checkout 会重写工作区。

方式一:包含了路径的用法

不会改变HEAD头指针,主要是用于指定版本的文件覆盖工作区中对应的文件

代码语言:javascript
复制
$ git checkout [-q] [<commit>] [--] <paths>...

是可选项,如果省略则相当于从暂存区(index)进行检出。这和上一章的重置命令大不相同:重置的默认值是 HEAD,而检出的默认值是暂存区。因此重置一般用于重置暂存区(除非使用--hard参数,否则不重置工作区),而检出命令主要是覆盖工作区(如果不省略,也会替换暂存区中相应的文件)。

方式二:不使用路径的用法

改变HEAD头指针。之所以后面的参数写作,是因为只有HEAD切换到一个分支才可以对提交进行跟踪,否则仍然会进入“分离头指针”的状态。在“分离头指针”状态下的提交不能被引用关联到而可能会丢失。

代码语言:javascript
复制
$ git checkout [<branch>]

关于 ”分离头指针“ HEAD指向的提交将作为新提交的父提交,查看当前HEAD的指向。 $ cat .git/HEAD 分离头指针,指的就是HEAD头指针指向了一个具体的提交ID,而不是一个引用(分支)。 $ git log --pretty=oneline -1 6f44ded81092794fc186e7869eadb4ca5ef225bb (HEAD -> master) test2 $ git checkout 6f44de # reflog是HEAD头指针的变迁记录,目前是非master分支 $ git reflog -1 6f44ded8 (HEAD, master) HEAD@{0}: checkout: moving from master to 6f44de $ cat .git/HEAD 6f44ded81092794fc186e7869eadb4ca5ef225bb

方式三:创建和切换到新的分支

新的分支从指定的提交开始创建。新分支和我们熟悉的master分支没有什么实质的不同,都是在refs/heads命名空间下的引用。

代码语言:javascript
复制
$ git checkout [-m] [[-b|--orphan] <new_branch>] [<start_point>]

注意,这里不能对远程分支进行checkout,远程分支不是真正意义上的分支,是类似于里程碑一样的引用。如果针对远程分支执行检出命令,会看到大段的错误警告。

分支–branch

代码语言:javascript
复制
# 基于远程分支创建本地分支的过程中
$ git checkout feature-2.2
$ git checkout -b feature-2.2 origin/feature-2.2
merge

会保留修改内容的历史记录。--no-ff(non fast-forward)总会生成一个merge commit,不添加该参数时,只有冲突时才会生成merge commit。

rebase

rebase 是在原有提交的基础上将差异内容反映进去。这个时候可能会有冲突,当出现冲突时,解决冲突后的提交不是使用 commit 命令,而是执行 rebase 命令指定 --continue 选项。若要取消 rebase,指定 --abort 选项。

注意: 变基操作的 实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。 如果你已经将提交推送至某个仓库,而其他人也已经从该仓库拉取提交并进行了后续工作,此时,如果你用 git rebase 命令重新整理了提交并再次推送,你的同伴因此将不得不再次将他们手头的工作与你的提交进行整合,如果接下来你还要拉取并整合他们修改过的提交,事情就会变得一团糟。

merge还是rebase? 有一种观点认为,仓库的提交历史即是 记录实际发生过什么。 它是针对历史的文档,本身就有价值,不能乱改,这些痕迹就应该被保留下来,让后人能够查阅。从这个角度看来,改变提交历史是一种亵渎。 另一种观点则正好相反,他们认为提交历史是 项目过程中发生的事。 没人会出版一本书的第一版草稿,软件维护手册也是需要反复修订才能方便使用。 总的原则是,只对尚未推送或分享给别人的本地修改执行变基操作清理历史,从不对已推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利。

cherry-pick
代码语言:javascript
复制
$ git cherry-pick <commitID>

从众多的提交中挑选出一个提交应用在当前的工作分支中。操作过程相当于将该提交导出为补丁文件,然后在当前HEAD上重放形成无论内容还是提交说明都一致的提交。对于从一个分支单独一个或者两个提交而不是合并整个分支的所有变更是非常有用的。可以通过git cherry-pick --abort 取消cherry-pick操作。

里程碑–tag

https://blog.csdn.net/ligang2585116/article/details/46468709

配置–config

版本库级别的配置文件 ~/.gitconfig,对应.git/config

代码语言:javascript
复制
$ git config -e 

全局配置文件--global(用户主目录下) ~/Documents/github/Blog/.git/config

代码语言:javascript
复制
$ git config -e --global 

系统级配置文件--system(/etc目录下) /private/etc/gitconfig

代码语言:javascript
复制
 $ git config -e --system

git config 使用INI文件格式。

  • 使用命令$ git config .,来读取INI配置文件中某个配置的键值;
  • 使用命令$ git config . ,来更改和设置INI配置文件中某个配置的值。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018/12/04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 版本库操作
    • 日志–log
      • 提交–commit
        • 对比变更
      • 获取–fetch or pull
        • fetch
        • pull
      • 推送–push
        • 重置–reset or reflog or revert
          • reset
          • reflog
          • revert
        • 检出–checkout
          • 方式一:包含了路径的用法
          • 方式二:不使用路径的用法
          • 方式三:创建和切换到新的分支
        • 分支–branch
          • merge
          • rebase
          • cherry-pick
        • 里程碑–tag
          • 配置–config
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档