多人场景Git演练

本文基于中国科学技术大学软件学院孟宁老师博客《五⼤场景玩转 Git,只要这一篇就够了!》

1.前言

入学后选择了孟宁老师的高级软件工程课,感觉孟宁老师讲的非常细致,并且提供了很多有用的课外资料,因此感谢孟宁老师细心指导。
那么接下来进入正题,讲讲我对git的理解~~

2.git是什么?

git 是一种分布式版本控制系统,能记录代码版本改动,具备团队协作功能,非常方便

3.几个概念

3.1. 工作区(Working Directory):在电脑里能看到的目录,你编辑代码的地方
3.2. 暂存区(Stage or Index):当前哪些改动是被git所追踪的(对的,不是所有修改都会被追踪,所以你需要把修改添加到暂存区,否则可能就会丢失)
3.3. 版本库(Repository):存储代码版本的仓库,在.git目录中(跟暂存区不同的是,把修改提交到这里会形成一个版本号,这样就能在git的分支树上体现出来了~~)

3.4. 远程库(Remote Repository):在服务器上存储代码的仓库。作为本地仓库的备份,也可以通过远程仓库进行多人协作,如果你不想因为意外丢掉你的代码,请一定记得把本地仓库的最新修改同步到远程仓库

4.多人场景演练

4.1 建立远程库

这个没啥好说的,在自己的github域名下建一个仓库就行

4.2 建立本地库


由于远程库中没有任何内容,需要新建本地库然后与远程库关联起来。如图,先新建本地仓库,再使用git init命令初始化本地仓库,如此一来,就可以在这个仓库里放代码了
接下来来点java代码试试?

public class GitTest {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

新增代码文件后,使用git add fileName命令,可以把代码文件从工作区添加进暂存区,接下来继续修改下代码文件,使用git status可以查看当前工作区,暂存区,和未追踪文件的状态

接下来使用git commit -m "第一次提交"把暂存区的文件提交到本地仓库中,这样就在本地形成一个版本号

4.3 关联远程库

git remote add origin https://github.com/JohnsonGreen/GitTest.git
git push -u origin master

在本地仓库增加远程库,并推送到远程库的master分支

4.4 建立个人开发分支

git checkout -b mybranch

不加-b参数则是直接切换到该分支。接下来我们新增一些代码,并推送到远程

public class GitTest {
    public static void main(String[] args) {
        System.out.println("Hello World!");
        System.out.println("Hello ChenHeng");
    }
}

此时使用git log --oneline --graph显示本地和远程都已经有两个节点

4.5 与同事协同开发

同事可以把远程仓库的内容完整的下载到本地,形成本地仓库,并建立他的分支。由于同事是个急性子,我才刚刚在远程master上推送了第一个结点"Hello World!",他就把代码clone下来了,并在此基础上建立了新的分支(不幸的是,我随后又在远程的master分支上增加了新的结点,猜猜他之后合并到master上会发生什么?)

git clone https://github.com/JohnsonGreen/GitTest.git
git branch -b hisbranch

加点代码试试

public class GitTest {
    public static void main(String[] args) {
        System.out.println("Hello World!");
        System.out.println("Hello ChenYuHong"); 
    }
}

然后推送到远程建立远程的hisbranch分支,并且此时想合并到远程的master分支,那么做法是,先在本地切换到master分支,然后使用git pull 将最新的内容更新到master分支,再使用 git merge --no-ff hisbranch (使用--no-ff参数可以在git分支树上看到分叉 )合并到本地的master分支,但是,能合并成功吗?

4.6 冲突解决

public class GitTest {
    public static void main(String[] args) {
        System.out.println("Hello World!");
<<<<<<< HEAD
        System.out.println("Hello ChenHeng");
=======
        System.out.println("Hello ChenYuHong");
>>>>>>> hisbranch
    }
}

我们看到,命令提示有冲突,并且代码中标注出了冲突位置,那这就需要选择保留哪一项了。保留之后,就可以add,commit以及push了。注意:在改同事代码的时候记得一定联系下他,否则可能被打~~
合并之后的分支树如图所示

4.7 吃后悔药

上次merge之后,有点后悔合并了,想还是继续回到第一次提交时"hello world"的状态,那么在通过git log --graph查找到该commit ID后,就可以使用下面这个命令完成回退啦!

git reset --hard 54a05c8

然后瞬间在master分支上只有一个结点了,惊不惊喜,意不意外?

4.8 吃后悔药的后悔药

经过上次回退之后,感觉又想把merge之后的代码找回来,但是此时只剩一个孤零零的"Hello World!",之前写的代码全丢了,这可咋办?
答案是依旧使用git reset --hard 命令,但是commit ID要怎么找?
然后时光机神器出现了

git reflog 

还好还好,有了git reflog这个能记录你所有git操作的大神器,妈妈再也不用担心我的代码丢失了

于是我们找到merge结点的commit ID,然后就可以乘坐时光机回到未来了


4.8 终结杂乱无章的提交

现在,我的同事要向孟宁老师打招呼,但是同事是个结巴,只能一个字母一个字母的打,于是我们在同事的提交记录上看到了一长串的commit,并且每个commit也就只打了一个字母

但是这一个一个字母形成的commit终究只是为了完成"Hello Meng Ning"这个打招呼的功能,真的有必要写这么多commit吗?
接下来我们使用git rebase -i [startCommitID] [endCommitId]这个命令,来让同事的招呼打得更顺畅一些。

git rebase -i dc8e07b

在上图中我们删除除了最后一条的所有commit,只保留最后一条commit
然后出现了冲突,如下

public class GitTest {
    public static void main(String[] args) {
        System.out.println("Hello World!");
        System.out.println("Hello ChenYuHong");
<<<<<<< HEAD
=======
        System.out.println("Hello Meng Ning");
>>>>>>> 1704756... 添加g
    }
}

我们当然要保留向孟宁老师打招呼的内容,修改之后把内容add进暂存区,并使用git rebase --continue完成rebase操作,此时会跳出编辑框,让我们把保留的这个commit的说明改一下,从"添加g"改为"向孟宁老师打招呼", 保存并提交,然后合并到master,并推送到远程库

从而我们在继续查看远程库的master分支时,就不会继续包含那些"结巴"的commit啦!

5. 一点小小的补充

5.1 为什么需要暂存区?

做某件事情的时候可能会改好几个文件,例如a、b、c三个文件,其中a和b文件是代码逻辑的修改,c文件是文案修改。这个时候为了让提交日志更可读,会先提交a和b,再提交c,于是就有如下的git操作

       git add a b
       git commit -m' change logic'
       git add c
       git commit -m 'change text'

如果没有暂存区这个东西,那只可能有一条提交,这个提交包含了逻辑调整和文案修改(可能是两件毫不相干的事情)

5.2 git push --force /-f 命令有什么安全问题?

--force 会使用本地分支的提交覆盖远端推送分支的提交。也就是说,如果其他人在相同的分支推送了新的提交,你的这一举动将“删除”他的那些提交!就算在强制推送之前先 fetch 并且 merge 或 rebase 了也是不安全的,因为这些操作到推送之间依然存在时间差,别人的提交可能发生在这个时间差之内.
2018年9月19日,一名程序员在美国某办公楼向4名同事开枪,导致一人情况危机,两人伤情严重,一人被子弹擦伤。目前,凶手已死,身份被警方查明。 目前,码农持枪杀人的动机仍然是个谜。有人猜测道:“同事不写注释,不遵循驼峰命名,括号换行,最主要还天天 git push -f 等因素” 激怒了这名行凶者。

5.3 git reset 和 git revert 有什么区别?

二者最大的区别是git revert是用新的一个commit来回滚之前的commit,HEAD是要继续前进,但是git reset是直接删除指定的HEAD,其是相当于在不断后退

5.4 版本回退之后,想吃后悔药怎么办?

    1) revert: 两次revert ,负负得正
    2) 如果知道回退之前的HEAD所指向的结点的版本号,那么依然可以使用 git reset --hard e475afc 来实现

5.5 正在dev分支进行开发,但是突然在master分支上有紧急bug要修复,怎么办?

    1)git stash save "增加readme.md2"
会将工作区和暂存区的内容储藏起来,前提是这些文件已经被git追踪
储藏未跟踪的文件:git stash save -ugit stash save --include-untracked
    2)git stash list
查看储藏列表

          stash@{0}: On dev: 修改为Yes
          stash@{1}: On dev: 增加readme.md2

    3)git stash apply1
应用最顶层的stash, 也可以指定哪个stash: git stash apply stash@{1}
git stash pop 与之类似,但会删除stashs
stash 之后又对同一个位置做了修改,造成冲突?
先对修改进行commit或者git stash
    4)git stash drop stash@{1}
删除某个stash或者git stash clear 清除所有stash
    5)git stash show -p stash@{1}
查看当前分支与某个stash的对比
    6)git stash branch newBranch stash@{1}
有的时候可以将储藏起来的代码应用到新建的分支,而不是当前分支上

5.6 如果只是想丢弃掉工作区的内容,该怎么做?

    1) git reset --hard HEAD
直接重置版本号(丢弃掉所有文件在工作区的修改 + 已经add到暂存区但没有commit的修改,需要谨慎使用
变种: git reset HEAD~1 或 git reset HEAD^ (均是回退到HEAD之前的一个版本)
    2)git checkout -- filename (针对选中的文件,可以添加多个)
这个操作总是让这个文件回到最近一次 git commit 或 git add 时的状态(commit之后修改没有add或add之后又做了修改),都是丢弃工作区的修改(修改包含删除操作)。
git checkout . 放弃当前目录下的修改
    3) 推荐使用git stash, 万一后悔了呢?

5.7 merge错了分支,咋办?

git merge --abort
该命令仅仅在合并后导致冲突时才使用。git merge --abort将会抛弃合并过程并且尝试重建合并前的状态。但是,当合并开始时如果存在未commit的文件,git merge --abort在某些情况下将无法重现合并前的状态

5.8 在发布版本号的时候,怎么给结点打标签?

    1)git tag -a v1.4 -m "my version 1.4"
会在当前分支上的HEAD结点打上打标签
    2) git tag -a v1.1 -m "my tag" a0472e6
给某一个结点打标签
    3) 推送标签到远程仓库
push单个tag,命令格式为git push origin [tagname]

posted @ 2020-10-07 21:32  陈煜弘  阅读(335)  评论(0编辑  收藏  举报