接上篇 Git汇总–对象及版本库存储
显示提交历史! 当不使用任何参数调用,相当于使用了缺省的参数HEAD,即显示当前HEAD能够访问到的所有历史提交。可以指定某个远程或者分支进行查看:
$ git log upstream/master
参数 | 说明 |
---|---|
--oneline | 最精简的日志输出 |
--graph | 分支图显示 |
- | 显示最近的几条日志 |
--stat | 显示每次提交的变更概要 |
--all | 显示所有分支的历史记录 |
commit 分为两种:一种是常规的 commit,也就是使用 git commit
提交的 commit;另一种是 merge commit,在使用 git merge
合并两个分支之后,你将会得到一个新的 merge commit。
merge commit 和普通 commit 的不同之处在于 merge commit 包含两个 parent commit,代表该 merge commit 是从哪两个 commit 合并过来的。
# cdc4a1为merge生成的commitID
$ git show cdc4a1
commit cdc4a1c252171c24b7c98b92a6fb94010637de44 (HEAD -> hotfix-inspection, origin/hotfix-inspection)
Merge: 81cf95f5 41cc17e6
使用命令git describe
将提交显示为一个易记的名称。
--g
(“基础版本号” - 距离“基础版本”的数字 - 该提交的SHA1哈希值缩写)的格式显示;--always
参数显示精简提交ID,否则出错。$ git describe
v2.0.0-143-gcffed5c2
补充:最后一次的提交信息,会存储在**.git/COMMIT_EDITMSG
** 中,这对于对提交信息格式校验很有帮助, 具体可以查看:Git提交信息规范化
$ cat .git/COMMIT_EDITMSG
feat(git): git总结
可以在commit命令后加参数-s
,为在提交说明的最后添加“Signed-off-by:”签名。
$ git commit -s -m "提交说明"
没有对工作区的文件进行任何修改,Git默认不会执行提交,参数--allow-empty
允许执行空白提交。
# 重新修改最新的提交,改正作者和提交者的错误信息
$ git commit --amend --allow-empty --reset-author
提交空文件夹: 默认情况下,Git不能对空文件夹进行提交。所以,我们可以在相关文件夹下创建一个文件
.gitkeep
来进行占位。
建议:
git stash
或其他分支保持当前进度上述提到的工作区、暂存区、HEAD,如何做比较呢?
命令 | 说明 |
---|---|
git diff | 工作区和暂存区比较 |
git diff --cached/--staged | 暂存区和HEAD(master)比较 |
git diff HEAD/master | 工作区和HEAD(当前工作分支,注意非远程) |
从另一个存储库下载对象和引用。
在执行git fetch命令的时候,可以通过 --no-tags
参数设置不获取里程碑只获取分支及提交
$ git fetch --no-tags
或在注册远程版本库的时候,使用--no-tags
参数避免将远程版本库的里程碑引入本地版本库
$ git remote add --no-tags
获取的引用名称及其指向的对象名称将写入.git / FETCH_HEAD
中。
$ cat .git/FETCH_HEAD
7ccd4971011fe8b0df6599efb38d011f25975979 branch 'master' of x.x.x.x:namespace/project-name
git fetch origin 就相当于执行了下面的命令,将远程版本库的所有分支复制为本地的远程分支
$ git fetch origin +refs/heads/*:refs/remotes/origin/*
示例:合并upstream/master提交到本地
获取到的提交会更新到本地跟踪共享版本库(远程)master分支的本地引用.git/refs/remotes/upstream/master
中
$ git fetch upstream master
# 合并操作
$ git merge upstream/master
git pull
又都做了哪些操作?
$ 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/*
$ git pull
git pull
相当于执行了git pull
branch..remote
,则为originremote..url
给出remote..fetch
配置了一个引用表达式,则使用该引用表达式执行获取操作branch..merge
,则对其设定的分支执行合并,否则报错退出所以,上述fetch示例可以通过下述命令替代
$ git pull upstream master
$ git push <remote> <branchname>
git push
branch..remote
,则为originremote..push
配置了一个引用表达式,则使用该引用表达式执行推送$ git push branch.<branchname>.remote branchname
个人建议: push默认的规则使得
git push
可以像git push origin
一样的效果,但是个人并不建议这种操作(你要明确你的确是想向origin提交;然后你的本地分支和远程分支名称必须一致<当然强烈建议创建分支本地和远程保持一致!>),以上,还是希望提供完整的命令!
.git/refs/heads/
中记录了其分支中对应的最新提交ID,下述为master分支最新提交ID:
$ cat .git/refs/heads/master
e695606fc5e31b2ff9038a48a3d363f4c21a3d86
重置命令git reset
的一个用途就是修改引用(如master)的游标。可以将“游标”指向任意一个存在的提交ID。
$ cat .git/HEAD
ref: refs/heads/master
在执行重置命令的时候没有使用任何参数对所要重置的分支名进行设置,这是因为重置命名实际上所针对的是头指针HEAD。
$ git reset [-q] [<commit>] [--] <paths>...
包含了路径的用法。不会重置引用,更不会改变工作区,而是用指定提交状态()下的文件()替换掉暂存区中的文件。
$ git reset HEAD/commitID [--] <filePaths>
注意: 为了避免路径和引用(或者提交ID)同名而冲突,可以在前用两个连续的短线作为分隔。
$ git reset [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
不使用路径的用法。会重置引用。根据不同的选项,可以对暂存区或者工作区进行重置。会彻底的丢弃历史(**git log
**查看不到提及历史)。
参数 | 影响范围 |
---|---|
–hard | 引用指向新的提交ID,替换暂存区和工作区 |
–soft | 只更改引用的指向,不改变暂存区和工作区 |
–mixed(缺省即为--mixed) | 更改引用的指向以及重置暂存区,但是不改变工作区 |
$ git reset --hard HEAD^
$ git reset --hard <commitID>
!!!注意: 使用重置命令很危险,会彻底的丢弃历史。那么还能够通过浏览提交历史的办法找到丢弃的提交ID,再使用重置命令恢复历史么?不可能!因为重置让提交历史也改变了。在一个共享的仓库中这会造成问题的。如果其他人已经有你将要重写的提交,你应当避免使用 reset;如果有任何其他提交在合并之后创建了,那么这个方法也会无效;移动引用实际上会丢失那些改动。
显示操作历史!
上述reset的第二种方式会丢失历史,如果真的做了上述操作,该如何还原呢?
$ 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
目录下日志文件记录了分支的变更。默认非裸版本库(带有工作区)都提供分支日志功能,这是因为带有工作区的版本库都有如下设置。
$ git config core.logallrefupdates
Git提供了一个**git reflog
** 命令,对这个文件进行操作。使用show
子命令可以显示此文件的内容。
$ 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哈希值
# 还原某个提交!!
$ git reset --hard HEAD@{2}
git log
与git reflog
区别:
git log
显示提交历史;git reflog
显示操作历史(包括已经被删除的 commit 记录和 reset 的操作)。revert 命令十分直观易用,相当于做一次被 revert 的提交的「反操作」并形成一个新的 commit。
上面commit中提到了,commit分为常规commit和merge的commit两种。所以,revert也对应两种方式:
使用 git revert
即可,git 会生成一个新的 commit,将指定的 commit 内容从当前分支上撤除
revert merge commit 这时需要添加 -m
选项以代表这次 revert 的是一个 merge commit,-m 选项接收的参数是一个数字,数字取值为 1 和 2,也就是 Merge 行里面列出来的第一个还是第二个。
$ 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/
git checkout
会重写工作区。
不会改变HEAD头指针,主要是用于指定版本的文件覆盖工作区中对应的文件
$ git checkout [-q] [<commit>] [--] <paths>...
是可选项,如果省略则相当于从暂存区(index)进行检出。这和上一章的重置命令大不相同:重置的默认值是 HEAD,而检出的默认值是暂存区。因此重置一般用于重置暂存区(除非使用--hard
参数,否则不重置工作区),而检出命令主要是覆盖工作区(如果不省略,也会替换暂存区中相应的文件)。
会改变HEAD头指针。之所以后面的参数写作,是因为只有HEAD切换到一个分支才可以对提交进行跟踪,否则仍然会进入“分离头指针”的状态。在“分离头指针”状态下的提交不能被引用关联到而可能会丢失。
$ 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
命名空间下的引用。
$ git checkout [-m] [[-b|--orphan] <new_branch>] [<start_point>]
注意,这里不能对远程分支进行checkout,远程分支不是真正意义上的分支,是类似于里程碑一样的引用。如果针对远程分支执行检出命令,会看到大段的错误警告。
# 基于远程分支创建本地分支的过程中
$ git checkout feature-2.2
$ git checkout -b feature-2.2 origin/feature-2.2
会保留修改内容的历史记录。--no-ff
(non fast-forward)总会生成一个merge commit,不添加该参数时,只有冲突时才会生成merge commit。
rebase 是在原有提交的基础上将差异内容反映进去。这个时候可能会有冲突,当出现冲突时,解决冲突后的提交不是使用 commit
命令,而是执行 rebase
命令指定 --continue
选项。若要取消 rebase,指定 --abort
选项。
注意: 变基操作的 实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。 如果你已经将提交推送至某个仓库,而其他人也已经从该仓库拉取提交并进行了后续工作,此时,如果你用 git rebase 命令重新整理了提交并再次推送,你的同伴因此将不得不再次将他们手头的工作与你的提交进行整合,如果接下来你还要拉取并整合他们修改过的提交,事情就会变得一团糟。
merge还是rebase? 有一种观点认为,仓库的提交历史即是 记录实际发生过什么。 它是针对历史的文档,本身就有价值,不能乱改,这些痕迹就应该被保留下来,让后人能够查阅。从这个角度看来,改变提交历史是一种亵渎。 另一种观点则正好相反,他们认为提交历史是 项目过程中发生的事。 没人会出版一本书的第一版草稿,软件维护手册也是需要反复修订才能方便使用。 总的原则是,只对尚未推送或分享给别人的本地修改执行变基操作清理历史,从不对已推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利。
$ git cherry-pick <commitID>
从众多的提交中挑选出一个提交应用在当前的工作分支中。操作过程相当于将该提交导出为补丁文件,然后在当前HEAD上重放形成无论内容还是提交说明都一致的提交。对于从一个分支单独一个或者两个提交而不是合并整个分支的所有变更是非常有用的。可以通过git cherry-pick --abort
取消cherry-pick操作。
https://blog.csdn.net/ligang2585116/article/details/46468709
版本库级别的配置文件 ~/.gitconfig
,对应.git/config
$ git config -e
全局配置文件--global
(用户主目录下) ~/Documents/github/Blog/.git/config
$ git config -e --global
系统级配置文件--system
(/etc目录下) /private/etc/gitconfig
$ git config -e --system
git config
使用INI文件格式。
$ git config .
,来读取INI配置文件中某个配置的键值;$ git config .
,来更改和设置INI配置文件中某个配置的值。