Git学习笔记

198 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情

image.png (注:图截取自青训营官方账号 Android 客户端专场学习资料一 git专题,原出处不详)

Git是什么

之前一直觉得vcs的v是指version,直到看到一篇参考文章(见文末),说git是distributed revision control system(分布式修改控制系统),感觉这个说法也很有意思。

同时,我认为和传统的vsc相比,git最大的优势在于 分布式 (支持远程协作)

Git仓库及其创建

git clone xxx 之后所在目录会生成一个文件夹,里面有所clone项目的代码,组成了工作树,.git包含了该项目的所有历史信息。

Git分支

Git的历史管理就像一棵树,有不同的分支(branch),一个commit创建一个新结点,每一个分支都可以通过一个head指针追踪到,每个head指针指向的是当前分支的末端(最新)(提前猜测版本回退的本质是指针前移)


刚新建的项目只有一个head分支(树的根),称为master。

有tag的说法,开发者用标签指明是哪个分支的哪个结点。


以下这行代码:
 git switch -c new v2.6.13

指的是新建一个分支(new v2.6.13)并切换过去(switch)并且查看 (-c) (我觉得切换的本质是指针跳转)


git branch

查看所有分支,当前分支会有*


 git reset --hard v2.6.17

强制切换当前分支至v2.6.17


git show

展示最近的代码提交记录

commit后面那段是一个唯一标识的id,通过哈希方法生成,和分支名字是唯一对应的。

(事实上,.git里文件数据和内容也是以hash形式存储的)



-   `git branch`

-   list all branches.

-   `git branch <branch>`

-   create a new branch named `<branch>`, referencing the same point in history as the current branch.

-   `git branch <branch> <start-point>`

-   create a new branch named `<branch>`, referencing `<start-point>`, which may be specified any way you like, including using a branch name or a tag name.

-   `git branch -d <branch>`

-   delete the branch `<branch>`; if the branch is not fully merged in its upstream branch or contained in the current branch, this command will fail with a warning.

-   `git branch -D <branch>`

-   delete the branch `<branch>` irrespective of its merged status.

-   `git switch <branch>`

-   make the current branch `<branch>`, updating the working directory to reflect the version referenced by `<branch>`.

-   `git switch -c <new> <start-point>`

-   create a new branch `<new>` referencing `<start-point>`, and check it out.

我总结一下用法:

git branch 默认在当前结点分叉,如果补上参数,就是在参数所在结点分叉; branch 后接-d是检查性删除,-D是强制性删除;

git switch是切换分支,git switch -c <new> <start-point>是在末尾参数所在结点分叉并且切换过去,并且检查。

.git文件架下的HEAD记录了谁是当前分支。


个人理解:

git switch --detach v2.6.17

指的是把该结点 分离 出来,单独查看其状态,在其上做的变动没有影响。

加上--detach的用处有两个:

  • 查看一些特殊版本。默认情况下switch会切到分支的head上,但是--detach可以让人很方便地查看某个任意的结点。
  • 谨慎一点,避免错误。--detach是分离的意思,也就是将要修改的结点先和主树切割开来,慢慢折腾,感觉没问题了可以使用
git switch -c new_branch_name

新建一个分支,将当前更改转移到新分支去。

Git远程分支

git branch命令后加上 -r 选项可以查看远程分支(remote)。

origin是指代远程仓库(即该仓库未被我们拉取的时候的状态)。

我的理解来自各个地方的提交信息会被集中记录,因此可以查到。

因为同一个仓库每时每刻都可能有来自全球各地的提交信息,因此可以通过“抓取”获得最新信息。它会更新关于其他远程提交的信息,但不会更改个人的本地仓库信息(即使原来的master在远程已经被改了也不会影响个人clone下来的master)。 git fetch

也可以通过拉取的方式git pull

(更正:这两种不是等同的,区别在于:)

  • git fetch只是拉取远程分支,但不直接与当前本地分支合并,需要手动合并。
  • git pull相当于fetch+merge

当个人推送上去之后,中央仓库记录也变多了,也能更新 。git push


也可以直接往远程仓库添加分支,这些记录也会被同步更新。
$ git remote add 分支名 

巧用二进制搜索(bisect)解决问题

问题背景

本地的某个分支版本运行正常,代码仓库里面的master出现了问题(说明是该分支版本之后的(某些)提交引起),如何快速通过代码提交历史定位差异?

解决方案

$ git bisect start
$ git bisect good v2.6.18
$ git bisect bad master

步骤: 开始搜索;告知哪个版本是好的,哪个版本是坏的。(good 和bad对应二分查找的首末位置)

系统是将代码版本结点隔离了再进行检查的。

检查完成后,会输出打印引起冲突的commit id号

后续

git bisect reset 回到未启动搜索前的分支

让bisect忽略某个版本的错误

  • 方法一
 git bisect visualize
 git reset --hard fb47ddb2db

这段代码的意思是先让这个版本对搜索程序显式可见,再 强行 让搜索程序将其认定为另一个安全且近似的版本fb47ddb2db

  • 方法二: 废话不多说,直接跳过
 git bisect skip

Git日志——查看历史commit

比如如下代码的意思是:查看2.5 以后 的版本中涉及Makefile文件 或 在fs目录下的

git log v2.5.. Makefile fs/

git log 和gitk都是很好的查看详情命令

找不同:diff

找xx和yy之间的不同

git diff xx..yy

找两者的祖先到yy的不同

git diff xx..。yy

找补丁

git format-patch master..test

Git merge xx合并分支

有冲突的部分会被单独拎出来,当冲突解决了之后再次提交,就能合并成功,该结点记属于原分支,也属于合并后的分支

放弃合并(中断合并): git merge --abort

撤销已经提交上去的合并请求(注意,使用要慎重,特别是该版本也合并了其他分支的情况下可能会出问题) git reset --hard xxx(原来分支的名字)

上述情况适用于不同的开发者独立开辟的分支的合并;如果一个分支从主枝延伸出来,现在要把它放回去,则称为“fast-forward”,内部实现过程没有上述那么复杂。

修改错误

两种方法:(推荐第一种)

  • 新建一个提交,相当于在原树上往前生成一个节点。 创建一个恢复旧版本的新结点 git revert xx
  • 回到原版本,在原版本上修改(!!如果代码已经公开了就不要再这么做!!)

git fsck (file system check) 一个有效检查仓库文件状态的命令


一些命令的运行流程

                      you push
your personal repo ------------------> your public repo
      ^                                     |
      |                                     |
      | you pull                            | they pull
      |                                     |
      |                                     |
      |               they push             V
their public repo <------------------- their repo

这样可以把私人提交和团队成果区分开,所以如果是这种方式的话,每次记得pull,不要埋头硬改。

push失败由什么引起?怎么解决?

  • 失败的原因是提交不是fast-forward的,有可能是因为之前强制修改了已经公开的提交。
  • 解决方案:强制push,以下两种任选其一
git push ssh://yourserver.com/~you/proj.git +master

git push -f ssh://yourserver.com/~you/proj.git master

和merge很像的rebase

rebase这个单词字面意思是重置,这里其实也是把分支树进行合并等变换 比如像下面这样:

                            H---I---J topicB
                           /
                  E---F---G  topicA
                 /
    A---B---C---D  master

输入命令

git rebase --onto master topicA topicB

会使树的结构变成这样:

                 H'--I'--J'  topicB
                /
                | E---F---G  topicA
                |/
    A---B---C---D  master

(感觉是个高级用法,它让我知道记录真的是可以随意修改的,不过我还是不要乱用比较好)

后记

之前对git的了解仅限于在Linux服务器命令行下创建了git仓库,学了几行简单命令(add、commit)这些方便提交lab;而正式在本机写代码时,由于之前写的东西体量小,不太需要版本控制,所以一般都是肉眼debug+凭借记忆,极少数情况会去用idea默认的vcs,翻翻上一次的修改版本。

但是现在慢慢开始要完成一些比较大的项目,而且需要团队合作,因此开始使用idea的git集成。

说实话,我感觉对着图标点点点是方便了,但是集成和封装好的东西反而让我有种很懵的感觉。所以想趁着有空,深入了解一下git,虽然它只是一个工具,但它的思想是很值得学习和研究的。认真了解一些细节也是为了更好地使用吧。

参考资料

Git User Manual