• Git


    1. 什么是 Git

    Git 是一个分布式版本控制系统,由 Linus Torvalds (也是 Linux 系统的创建者)编写。最主要的两个特点就是:

    • 版本管理:对每一次版本进行管理,可以对产品版本进行任意回滚,即每修改一次就保存一次,以防止丢失工作进度。
    • 协作开发:实际开发中一般都是多人协同开发,每个人负责不同的方面,为确保整体工作进度,这就需要一个工具来保证代码时刻是最新的,Git 就是这样的一个工具。

    常用版本管理工具

    • VSS
    • CVS
    • SVN
    • GIT
    • BitKeeper

    Tips:

    所有版本控制系统都只能控制跟踪文本文件的改动,如:TXT 文件、代码程序、网页代码等,对于二进制文件只能知道其修改了,但是不能知道具体修改了什么。

    另外最好使用 utf-8 通用编码格式。

    2. Git 基本操作

    2.1 安装

    Linux 上安装 Git

    sudo apt-get install git
    

    Windows 上直接下载安装即可,记得添加系统环境变量。

    2.2 创建版本库

    所谓版本库即仓房文件/代码的仓库 repository ,可以理解为一个目录。

    首页我们要选择一个合适的地方,创建一个空目录,切换到目录中,然后初始化 Git,这样就创建好了一个版本库。

    $ cd E:
    $ mkdir git_test
    $ git init    # 初始化 Git
    

    创建成功后,会看到多出了一个 .git 的目录,这是用于跟踪管理版本库,切勿删除。Linux 中看不到可以使用 ls -ah 查看(隐藏文件)

    将文件添加到版本库

    只有将文件存放在版本库中,Git 才能管理控制。

    1. 使用 git add 命令将文件添加到暂存区
    $ $ vim test.txt
    $ git add test.txt        # git add . 添加多个文件
    
    # 添加一句:第一行代码,第一次修改
    
    warning: LF will be replaced by CRLF in test.txt.
    The file will have its original line endings in your working directory
    
    1. add 命令只能将文件添加到暂存区,并不是真正放到仓库中,还需使用 git commit 命令才能将文件真真添加到仓库中
    # m 后面跟修改备注,以便后续查询
    $ git commit -m '第一次修改'
    
    [master d25c1c9] 第一次修改
     1 file changed, 1 insertion(+)
     create mode 100644 test.txt
    

    第一次提交可能会需要输入邮箱和用户名(任意),这只是 Git 为了方便区分是谁提交或修改的文件。

    2.3 版本回滚

    现在我们来修改 test.txt,再添加一行:

    $ vim test.txt
    
    # 在第二行添加:第二行代码,第二次修改
    

    提交之前可以用 git status 查看状态,查看修改了哪些内容,再提交:

    $ git status
    
    On branch master
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
    
            modified:   test.txt
    
    no changes added to commit (use "git add" and/or "git commit -a")
    

    可以看到 git 提示我们修改了 test.txt 文件,并且贴心地告诉我们使用 git add filegit commit 去提交。

    提交后我们再使用 git status 查看会发现工作目录是干净的:

    $ git status
    
    On branch master
    nothing to commit, working tree clean
    

    版本控制

    实际开发中,我们可能每天都要提交最新的代码,长久以往我们不断修改提交、提交修改。当有一天不小心把文件/代码改乱了,想回到之前某个的版本,那么该怎么办?放心,Git 早已帮你记录好每次提交的版本,你只需查看版本号就可回到相应到版本。这样也就不怕代码会丢失。

    $ git log
    
    commit 8e882ba384400d48d76345e3b3d4d7a30fabda49 (HEAD -> master)
    Author: Hubery_Jun <9825xxx16@qq.com>
    Date:   Fri May 3 22:55:30 2019 +0800
    
        第二次修改
    
    commit d25c1c9b41eba07b4a50fdbc77e15f6313d003d7
    Author: Hubery_Jun <9825xxx16@qq.com>
    Date:   Fri May 3 22:44:27 2019 +0800
    
        第一次修改
    
    commit a069466807ba3f87a0d68290614a2498770c2675
    Author: Hubery_Jun <9825xxx16@qq.com>
    Date:   Wed May 1 16:51:42 2019 +0800
    
    # git diff 命令查看修改了什么内容
    

    使用 git log 可以查看每次提交时的记录,版本号(最长的那串 md5 值),以及修改者与时间。

    如果只想查看版本号和提交时的备注,可以用:

    $ git log --pretty=oneline
    
    8e882ba384400d48d76345e3b3d4d7a30fabda49 (HEAD -> master) 第二次修改
    d25c1c9b41eba07b4a50fdbc77e15f6313d003d7 第一次修改
    
    

    版本回退

    # 回到上一个版本
    $ git reset --hard HEAD^
    
    # 回到指定版本,后面跟版本后
    $ git reset --hard 8e882ba384400d48d76345e3b3d4d7a30fabda49
    
    # 实际只需要前 6 位即可
    $ git reset --hard 8e882b
    
    

    现在我们将 text.text 回滚到上一个版本:

    $ git reset --hard d25c1c
    
    HEAD is now at d25c1c9 第一次修改
    
    $ cat test.txt
    第一行代码,第一次修改
    
    

    再查看提交记录,发现只有第一次提交记录,我们已经成功回滚到了第一个版本:

    $ git log
    
    commit d25c1c9b41eba07b4a50fdbc77e15f6313d003d7 (HEAD -> master)
    Author: Hubery_Jun <982562616@qq.com>
    Date:   Fri May 3 22:44:27 2019 +0800
    
        第一次修改
    
    

    等等,要是手贱回滚错误,后悔了又想回滚回去怎么办?如果没有关闭 Git Bash,还可以往上翻查看上一次的提交版本号,但要是不小心把 Git Bash 关闭掉了怎么办?

    不用担心,Git 总是那么贴心,我们只需使用 git reflog 查看所有的历史提交记录,即使已经回滚过的版本,并且还提示你上一次回滚的版本:

    $ git reflog
    
    d25c1c9 (HEAD -> master) HEAD@{0}: reset: moving to d25c1c
    8e882ba HEAD@{1}: commit: 第二次修改
    d25c1c9 (HEAD -> master) HEAD@{2}: commit: 第一次修改
    
    

    3. 工作区、暂存区和版本库

    前面我们有提到过暂存区和版本库,然而没有实际解释过两者到底是什么。

    • 工作区:存放文件的目录,即起初我们创建的 git_test 目录,所有的要修改的文件以及版本库到在这里面。
    • 版本库:工作区中有一个隐藏目录 .git,它就是版本库,用来进行版本控制,里面包含暂存区和分支 branch 等。

    多说无益,还是上图吧(stage:暂存区,master:默认的主分支):

    前面我们说把一个文件提交到版本库分为两步(add、commit),现在从上图可以清晰地看出:

    • 第一步使用 git add file 只是将文件添加到暂存区 stage 中,并没有真正提交到版本库中
    • 第二步 git commit,才是真正的将文件提交到版本库中(实际是提交到 master 分支上,如果没有创建别的分支的话)

    4. 撤销修改

    实际开发中需要不断地修改提交,难免会出现错误,同样地 Git 也提供了后悔药,而且有两次:

    • 未添加到暂存去之前,即还在工作区
    • 已经添加到暂存区,但未提交到分支上

    4.1 工作区撤销修改

    首先我们修改 test.txt 文件,在最后添加一行:

    $ vim test.txt
    
    hj@DESKTOP-IHSAOT4 MINGW64 /e/git_test (master)
    $ cat test.txt
    
    第一行代码,第一次修改
    第二行代码,第二次修改
    撤销修改        # 新添加的
    
    

    使用 git status 命令查看状态,发现 Git 提示可以使用 add 将文件修改添加到暂存区或使用 git checkout -- <file> 命令去撤销工作区的修改。

    hj@DESKTOP-IHSAOT4 MINGW64 /e/git_test (master)
    $ git status
    
    On branch master
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
    
            modified:   test.txt
    
    no changes added to commit (use "git add" and/or "git commit -a")
    
    

    这里我们选择撤销修改,再次查看状态发现工作区是干净的,修改也成功撤销:

    hj@DESKTOP-IHSAOT4 MINGW64 /e/git_test (master)
    $ git checkout -- test.txt
    
    hj@DESKTOP-IHSAOT4 MINGW64 /e/git_test (master)
    $ cat test.txt
    
    第一行代码,第一次修改
    第二行代码,第二次修改
    
    

    4.2 暂存区撤销

    要是万一不小心修改已经添加到暂存区了怎么办?不要方,Git 还有一招,当然也是最后一道防线了。

    我们再修改 test.txt 文件,添加:

    hj@DESKTOP-IHSAOT4 MINGW64 /e/git_test (master)
    $ vim test.txt
    
    hj@DESKTOP-IHSAOT4 MINGW64 /e/git_test (master)
    $ cat test.txt
    第一行代码,第一次修改
    第二行代码,第二次修改
    添加到暂存区,怎么撤销?    # 新添加
    
    

    再查看其状态:

    $ git status
    
    On branch master
    Changes to be committed:
      (use "git reset HEAD <file>..." to unstage)
    
            modified:   test.txt
    
    

    Git 告诉我们可以使用 git reset HEAD <file> 回到不在暂存区的位置(unstage),即工作区的位置。

    hj@DESKTOP-IHSAOT4 MINGW64 /e/git_test (master)
    $ git reset HEAD test.txt
    
    Unstaged changes after reset:
    M       test.txt
    
    # 再查看状态,发现和工作区撤销是一样的了
    
    $ git status
    
    On branch master
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
    
            modified:   test.txt
    
    no changes added to commit (use "git add" and/or "git commit -a")
    
    hj@DESKTOP-IHSAOT4 MINGW64 /e/git_test (master)
    $ git checkout -- test.txt
    
    hj@DESKTOP-IHSAOT4 MINGW64 /e/git_test (master)
    $ git status
    On branch master
    nothing to commit, working tree clean
    
    

    4.3 总结

    • 撤销分为两种:工作区、暂存区撤销
    • 工作区撤销: git checkout -- 文件名
    • 暂存区撤销:git reset HEAD 文件名,只能撤销到工作区,还要再执行 git checkout -- 文件名 才能真正撤销

    5. 删除操作

    我们经常会删除一些没用的文件,或者一不小心删除错了某个文件,该怎么办?

    1. 首先我们新建一个 t2.txt 的新文件,在里面添加一行:
    $ vim t2.txt
    $ cat t2.txt
    删除操作
    
    # 然后再把 t2 添加提交到版本库中
    $ git add .
    warning: LF will be replaced by CRLF in t2.txt.
    The file will have its original line endings in your working directory
    
    
    $ git commit -m '新增一个 t2.txt 文件'
    [master 4fbaaa8] 新增一个 t2.txt 文件
     1 file changed, 1 insertion(+)
     create mode 100644 t2.txt
    
    
    1. 现在我们使用 rm 命令来尝试删除 t2.txt
    $ rm t2.txt
    
    # 再查看它的状态
    $ git status
    
    On branch master
    Changes not staged for commit:
      (use "git add/rm <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
    
            deleted:    t2.txt
    
    no changes added to commit (use "git add" and/or "git commit -a")
    
    

    查看状态,发现 Git 提示我们有两个选择,要么就是继续使用 add/rm 命令再提交来完成删除操作,要么就是使用 git checkout -- file 来恢复删除,可见 Git 是多么的贴心。

    1. 这里我们选择继续删除:
    $ git rm t2.txt
    rm 't2.txt'
    
    # 执行删除命令后,一定要再提交,否则无效
    $ git commit -m '删除 t2.txt'
    [master ccd5c05] 删除 t2.txt
     1 file changed, 1 deletion(-)
     delete mode 100644 t2.txt
    
    # 再查看工作区,发现 t2.txt 已经被删除
    $ ls
    test.txt
    
    

    总结

    • 使用 rm 命令删除的文件在未真正删除之前,也是可以还原的
    • git rm file 后一定要执行 commit 才能生效
    • git checkout 本质是将版本库中版本替换工作区版本,因此可以还原

    6. 远程仓库 GitHub

    Git 另一个功能就是 远程仓库,只要保证有一台机器有一个原始仓库,其他机器便可克隆这个原始版本库,理论上可以克隆无数份。

    原始仓库所在机器与克隆的机器理论上可以同一台,但是若是这台机器挂了,那么两者都会挂掉,这样做好像没什么意义。实际开发中有两种方法:

    • 公司内部专门有一台机器用来充当原始仓库(服务器),其他人可以从上面克隆这个仓库
    • 还有一种就是免费好用的 GitHub ,它可以免费托管代码,但是它是公开的,当然你也可以花点钱把你的仓库变为私有。

    6.1 将 Git 与 GitHub 建立通讯

    GitHub 与 Git 之间是才用 SSH 加密通讯的,如果想克隆 GitHub 上面的代码,就需要每次都输入用户名和密码。当然还有一种方式可以不用输入密码,那就是 SSH Keys

    SSH Keys

    SSH Keys 就像一段暗号,通讯时只需要检查暗号是否正确即可,主要分为以下几步:

    1. 在目标机器创建 SSH Key,首先在用户主目录检查下是否有 .ssh 目录,再看看里面是否有 id_rsa 和 id_rsa.pub 两个文件,若有则忽略这一步:
    # 打开 shell 或 Git Bash,输入以下命令,邮箱换成自己的,一路回车即可。成功后会提示 .ssh 目录的位置,Windows 一般在 /c/Users/用户名/.ssh
    
    # id_rsa 是私匙切忌泄露,id_rsa.pub 是公匙,可以给别人看
    $ ssh-keygent -t rsa -C 'youremail@example.com'
    
    
    1. 将公匙复制到 GitHub

    可以建立多个 SSH key,家里和公司都可以提交。

    6.2 创建远程仓库

    打开 GitHub 首页,登录点击创建一个新的仓库:

    创建成功后,就会出现一些界面,它会提示你怎样将本地仓库推送到远程仓库:

    现在我们把本地 git_test 目录下的 test.txt 推到这个远程仓库中去:

    $ git remote add origin git@github.com:hj1933/git_test.git
    
    # 推送到远程仓库,origin 是远程仓库名,你也可以修改,推送成功后悔看到如下提示
    
    $ git push -u origin master
    
    The authenticity of host 'github.com (13.229.188.59)' can't be established.
    RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8.
    Are you sure you want to continue connecting (yes/no)? yes
    Warning: Permanently added 'github.com,13.229.188.59' (RSA) to the list of known hosts.
    Enumerating objects: 19, done.
    Counting objects: 100% (19/19), done.
    Delta compression using up to 4 threads
    Compressing objects: 100% (13/13), done.
    Writing objects: 100% (19/19), 1.58 KiB | 202.00 KiB/s, done.
    Total 19 (delta 4), reused 0 (delta 0)
    remote: Resolving deltas: 100% (4/4), done.
    To github.com:hj1933/git_test.git
     * [new branch]      master -> master
    Branch 'master' set up to track remote branch 'master' from 'origin'.
    
    

    推送成功后,打开 GitHub,找到刚才新建的 git_test 仓库,刷新下是不是发现多了一个 test.txt 的新文件?就是这么简单快捷。

    6.3 从远程仓库克隆到本地

    打开远程仓库,点击右边 Clone or download,复制方框中的链接。然后再本地找一个适合的位置,打开 Git Bash,输入:

    # 记得将链接替换成自己仓库的链接
    
    git clone git@github.com:hj1933/git_test.git
    
    

    Git 还提供了 Https 协议的方式,相对 SSH 来说一点慢,而且每次都需要输入口令。但是对于有些公司来说只开放 http 端口,而无法使用 SSH 协议。

    克隆成功后,会发现本地目录中多了一个 git_test 的仓库,这就和我们自己用命令在本地创建仓库是一样的,至此我们也就可以和远程仓库互相通信了。

    7. 分支管理

    分支就像小说中的分身术,本尊与分身可以同时学习新的东西,分身学会了本尊亦懂。

    分支管理是 Git 的一个非常重要的功能,虽然其他的版本控制系统(如:SVN)也有分支管理,但是创建、合并或删除分支时,速度特别慢,而 Git 无论哪个操作都可以在 1s 内完成,简直完虐其他软件。

    分支在实际开发中的作用

    实际开发一般都是多人协同开发,每个人负责不同的功能模块,假如都在一个分支上开发。那么后面的开发人员必须等前面的开发人员开发完毕才能工作,这就会导致浪费大量时间。

    而有了分支后,每个人都可以创建自己的分支,然后在自己分支上修改提交都不会影响主分区,待开发完毕再合并到主分支即可,这样就能保证了整体的工作进度。

    7.1 创建分支

    Git 把每个版本串成一条线,就好比时间线一样(一直往前走),这条线就是一个分支,默认情况下只有一个 主分支 master

    其中 HEAD 是指针,要回滚到哪个版本或切换到哪个分支,它就会指向谁。严格意义上来说 HEAD 不是指向提交,而是指向分支,分支才指向提交。

    另外因为 HEAD 是 C 语言中的指针,每次切换到其他分支时也就会特别快,这也就是 Git 为什么会比其他版本控制系统快的原因。

    每一次提交,master 就会往前走一步,不断提交 master 分支也会越来越长。当我们创建新的分支时,HEAD 就会指向新的分支 dev

    dev 分支上的任务完成后,就可以与 master 主分支合并了,HEAD 也会重新指向 master,从而并不影响整体进度。


    创建第一个分支

    1. 使用 git checkout -b 分支名 快速创建一个新的分支:
    # 创建一个新的分支 dev,并切换到 dev
    $ git checkout -b dev   # dev 为新的分支名
    Switched to a new branch 'dev'
    
    # 事实上上面这条命令是下面这两条命令的简写
    $ git branch dev
    $ git checkout dev
    
    
    1. 查看当前所在分支,并修改 test.txt
    # 查看所有分支,带星号的为当前分支
    $ git branch
    * dev
      master
    
    # 可以看出分支 dev 下也有个 test.txt,它从 master 主分支 copy 了一份
    $ ls
    test.txt
    
    # 修改 test.txt,在最后添加一行
    $ vim test.txt
    
    $ cat test.txt
    
    第一行代码,第一次修改
    第二行代码,第二次修改
    分支测试
    
    
    1. 添加提交到版本库后,重新切换回 master 主分支:
    $ git add .
    
    $ git commit -m '第一次分支测试'
    
    [dev 4fa1810] 第一次分支测试
     1 file changed, 1 insertion(+)
    
    # 切换到 master 分支
    $ git checkout master
    Switched to branch 'master'
    Your branch is up to date with 'origin/master'.
    
    # 查看当前分支
    $ git branch
      dev
    * master
    
    
    1. dev 分支合并到 master 分支:
    # 合并分支
    $ git merge dev
    Updating ccd5c05..4fa1810
    Fast-forward
     test.txt | 1 +
     1 file changed, 1 insertion(+)
    
    # 查看 test.txt,发现在 dev 的修改同步到了 master 分支
    $ cat test.txt
    第一行代码,第一次修改
    第二行代码,第二次修改
    分支测试
    
    
    1. 删除分支,合并之后如果没有其他用,就可以删除 dev 分支了:
    # 删除 dev 分支
    $ git branch -d dev
    Deleted branch dev (was 4fa1810).
    
    
    $ git branch
    * master
    
    

    总结

    • 创建并切换分支:git checkout -b 分支名
    • 查看所有分支:git branch,带 * 号的胃当前分支
    • 切换分支:git checkout 分支名
    • 合并分支:git merge 分支名
    • 删除分支:git branch -d 分支名
    • 合并到 master 分支时,一定要先切换到 master 分支

    7.2 解决分支冲突

    1. 添加一个新的分支 feature1,并在最后一行添加:
    $ git checkout -b feature1
    Switched to a new branch 'feature1'
    
    
    $ vim test.txt
    
    $ cat test.txt
    第一行代码,第一次修改
    第二行代码,第二次修改
    分支测试
    添加一这行,用于新的分支 feature1     # 新添加的
    
    
    $ git add .
    
    # 提交到版本库
    $ git commit -m '添加新分支 feature1'
    [feature1 93426de] 添加新分支 feature1
     1 file changed, 1 insertion(+)
    
    
    1. 切换到 master 分支,并修改 test.txt
    $ git checkout master
    
    # Git 告诉我们当前 master 分支比远程的 master 分支提交一个分支
    Switched to branch 'master'
    Your branch is ahead of 'origin/master' by 1 commit.
      (use "git push" to publish your local commits)
    
    
    # 打开 test.txt,在最后添加一行
    $ vim test.txt
    
    
    $ cat test.txt
    第一行代码,第一次修改
    第二行代码,第二次修改
    分支测试
    master 分支提交   # 新添加的
    
    

    现在 masterfeature1 分支上都有提交,整体流程变成这样:

    1. 提交后,尝试合并:
    $ git add .
    
    
    $ git commit -m 'master 更新'
    [master ae39a38] master 更新
     1 file changed, 1 insertion(+)
    
    # 合并分支时发现有一个冲突:在 test.txt
    $ git merge feature1
    Auto-merging test.txt
    CONFLICT (content): Merge conflict in test.txt
    Automatic merge failed; fix conflicts and then commit the result.
    
    
    1. git status 查看冲突的地方:
    $ git status
    On branch master
    Your branch is ahead of 'origin/master' by 2 commits.
      (use "git push" to publish your local commits)
    
    You have unmerged paths.
      (fix conflicts and run "git commit")
      (use "git merge --abort" to abort the merge)
    
    Unmerged paths:
      (use "git add <file>..." to mark resolution)
    
            both modified:   test.txt
    
    no changes added to commit (use "git add" and/or "git commit -a")
    
    

    Git 告诉我们当前 master 分支比远程 master 分支超出两个提交,冲突的文件为 test.txt,并告诉我们两个分支上都做了修改。

    1. 重新打开 test.txt 文件,找到冲突的地方:
    $ cat test.txt
    第一行代码,第一次修改
    第二行代码,第二次修改
    分支测试
    <<<<<<< HEAD
    master 分支提交
    =======
    添加一这行,用于新的分支 feature1
    >>>>>>> feature1
    
    

    可以看到我们在 master 和 feature1 分支上做的修改,我们删除冲突,再重新提交:

    # 删除标记(提示内容),再重新提交
    $ cat test.txt
    第一行代码,第一次修改
    第二行代码,第二次修改
    分支测试
    master 分支提交
    添加一这行,用于新的分支 feature1
    
    
    $ git add .
    
    $ git commit -m '解决冲突'
    [master 6fa9b73] 解决冲突
    
    

    冲突解决后,分支也自然合并成功,再重新打开 test.txt,看看是否合并成功。上面步骤相当于将 feature1 与最新的 master 合并。

    也可以使用 git log 查看合并情况:

    $ git log --graph --pretty=oneline
    *   6fa9b73c86d0182bc960c308e224566f0c07cdfe (HEAD -> master) 解决冲突
    |
    | * 93426de6f45737ce835fb85f912b1d50f1b4a137 (feature1) 添加新分支 feature1
    * | ae39a38842ca7f216acffe0edaeadbdbae953873 master 更新
    |/
    * 4fa18106952d27cf15eeba018d739d37b3375345 第一次分支测试
    * ccd5c0587a0cbf97755e70edcc7b0acc80d51aa2 (origin/master) 删除 t2.txt
    * 4fbaaa813446fc4554c819e502aab904d8598a84 新增一个 t2.txt 文件
    * 8e882ba384400d48d76345e3b3d4d7a30fabda49 第二次修改
    * d25c1c9b41eba07b4a50fdbc77e15f6313d003d7 第一次修改
    
    

    总结

    • 如果分支上有提交,master 分支上没有提交,那么合并时就不会有冲突
    • 若两者都有提交,合并时就会有冲突,可以使用 git status 查看冲突的文件,然后再修改解决冲突即可
    • 使用 git log --graph --pretty=oneline 命令可以查看分支合并情况

    7.3 分支策略

    实际开发中应该遵循基本原则进行分支管理:

    • master 分支是最稳定的分支,一般在上面开发,仅用于发布最新稳定版
    • 日常情况开发人员都会在 dev 分支上开发,在合适的时候再合并到 master 分支上去发布
    • 另外每个开发人员都会有自己的独立分支,如:roselila 等等,时不时往 dev 分支合并。

    整体流程大致像如下所示:

    7.4 bug 分支

    所谓 bug 分支,一般是用于临时处理某个 bug 而创建的分支,bug 处理完毕后也会被删除。

    假设当前正在 dev 分支上开发,目测还有一周时间才能完成。这时老板突然要你赶紧处理一个 bug(估计两小时就能完成),但是手头上的工作还没完成,如若提交就会影响其他人的进度,那么该怎么办呢?

    不用方,Git 给我们提供了一个 隐藏功能,我们可以将当前工作临时隐藏起来,待处理完 bug 后再恢复即可。


    下面我们来模拟一下:

    1. rose 正在 dev 分支上开发一个功能,刚写了 func() 函数:
    $ cat test.txt
    第一行代码,第一次修改
    第二行代码,第二次修改
    分支测试
    master 分支提交
    添加一这行,用于新的分支 feature1
    def func():           #  这个函数刚写的
          print('正常的开发!')
    
    
    # 接到 Boss 电话要赶紧在两小时内修复一个小 bug,但是现在手头上的代码才写了一部分,没有提交
    $ git status
    On branch dev
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
    
            modified:   test.txt
    
    no changes added to commit (use "git add" and/or "git commit -a")
    
    
    1. 使用 git stash 命令将当前工作隐藏起来,并切换回到 master 分支上,创建一个新的 bug 分支 bug-001
    # 将当前工作隐藏起来
    $ git stash
    Saved working directory and index state WIP on dev: 3cc1284 巴拉巴拉
    
    # 再查看状态,发现已经是干净的了
    $ git status
    On branch dev
    nothing to commit, working tree clean
    
    # 切换回 master 分支上
    $ git checkout master
    Switched to branch 'master'
    Your branch is ahead of 'origin/master' by 7 commits.
      (use "git push" to publish your local commits)
    
    # 创建一个新的 bug 分支,bug-001
    $ git checkout -b bug-001
    Switched to a new branch 'bug-001'
    
    
    1. 修改 test.txt,在最后一行添加一个 哈哈哈哈
    $ vim test.txt
    
    $ cat test.txt
    第一行代码,第一次修改
    第二行代码,第二次修改
    分支测试
    master 分支提交
    添加一这行,用于新的分支 feature1,哈哈哈哈
    
    # 然后再添加提交,并切换 master 分支,合并
    $ git add .
    
    $ git commit -m 'bug-001 解决'
    [bug-001 706dcb2] bug-001 解决
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    # 切换到 master 分支
    $ git checkout master
    Switched to branch 'master'
    Your branch is ahead of 'origin/master' by 7 commits.
      (use "git push" to publish your local commits)
    
    # 合并 bug-001
    $ git merge bug-001
    Updating 3cc1284..706dcb2
    Fast-forward
     test.txt | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    $ cat test.txt
    第一行代码,第一次修改
    第二行代码,第二次修改
    分支测试
    master 分支提交
    添加一这行,用于新的分支 feature1,哈哈哈哈
    
    # 删除 bug-001
    $ git branch -d bug-001
    Deleted branch bug-001 (was 706dcb2).
    
    
    1. 最后切换到 dev 分支,重新开始工作:
    # 切换到 dev
    $ git checkout dev
    Switched to branch 'dev'
    
    # 查看当前所在分支
    $ git branch
    * dev
      feature1
      master
    
    # 发现工作区是干净的
    $ git status
    On branch dev
    nothing to commit, working tree clean
    
    # 用 git stash list 命令查看之前被隐藏的工作现场
    $ git stash list
    stash@{0}: WIP on dev: 3cc1284 巴拉巴拉
    
    # 恢复之前的 “工作现场”(stash 内容)
    $ git stash apply
    On branch dev
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
    
            modified:   test.txt
    
    no changes added to commit (use "git add" and/or "git commit -a")
    
    
    # 删除 stash 内容
    $ git stash drop
    Dropped refs/stash@{0} (81ebdd4a9e65e1b4fef243e4f61e593d40eca482)
    
    # 再查看 stash,已经没有了
    $ git stash list
    
    

    恢复 stash 内容时,会有两种选择:

    • git stash apply 恢复 stash 内容,但是并不会删除,还需要 git stash drop 删除
    • git stash pop 恢复的同时也将 stash 删除掉。
    • stash 内容可以理解为隐藏工作现场的信息,可删除

    总结

    • 当要处理某个 bug,手头上工作又还没完成时,可以使用 git stash 将当前工作隐藏起来
    • 解决 bug,最好创建一个 bug 分支,完成后即可删除
    • 恢复之前被隐藏的 stash 内容,可用 git stash apply,查看被隐藏的内容可用 git stash list,删除 git stash drop,恢复并删除 git stash pop

    8. 多人协作

    当踩远程仓库克隆岛本地仓库后,本地的 master 分支就和远程 master 分支相关联,远程仓库的默认名称为 origin

    查看远程仓库信息:

    # 查看远程仓库名字
    $ git remote
    origin
    
    # 查看更详细信息,显示抓取 fetch,和推送的地址
    $ git remote -v
    origin  git@github.com:hj1933/git_test.git (fetch)
    origin  git@github.com:hj1933/git_test.git (push)
    
    

    8.1 推送分支

    如何将本地仓库代码推送到远程仓库,需要指定远程仓库名字和要推送的分支。

    # 推送 master 分支,也可以是其他分支,如 dev、feature 等
    $ git push origin master
    Enumerating objects: 32, done.
    Counting objects: 100% (32/32), done.
    Delta compression using up to 4 threads
    Compressing objects: 100% (30/30), done.
    Writing objects: 100% (30/30), 2.47 KiB | 252.00 KiB/s, done.
    Total 30 (delta 21), reused 0 (delta 0)
    remote: Resolving deltas: 100% (21/21), completed with 1 local object.
    To github.com:hj1933/git_test.git
       ccd5c05..6446ce4  master -> master
    
    

    不是所有分支都需要网远程推送:

    • master 分支为主分支,要与远程同步
    • dev 分支是开发分支,开发人员在上面工作,也要与远程同步
    • bug 分支只用于本地修复 bug,无需推送

    8.2 抓取分支

    日常开发中,大家都会往 masterdev 分支上推送各自的修改,下面来模拟下多人协作开发,以及遇到的问题该怎么解决。

    情景:公司有一个远程仓库 git_test,现在 rose 和 bob 都把它克隆到自己的本地仓库中。

    1. rose 和 bob 分别将远程仓库克隆岛本地
    $ git clone git@github.com:hj1933/git_test.git
    Cloning into 'git_test'...
    remote: Enumerating objects: 49, done.
    remote: Counting objects: 100% (49/49), done.
    remote: Compressing objects: 100% (19/19), done.
    remote: Total 49 (delta 26), reused 47 (delta 24), pack-reused 0
    Receiving objects: 100% (49/49), done.
    Resolving deltas: 100% (26/26), done.
    
    
    1. 一般克隆成功后,都只有一个 master分支,没有别的分支。然后自己再创建一个 dev 分支,然后时不时把 dev 分支 。

    bob 在本地创建了一个 dev 分支,在 test.txt最后添加了一句 另一个小伙伴开发,然后添加提交并推送到远程:
    push 到远程。

    $ cd git_test
    
    $ git checkout -b dev
    Switched to a new branch 'dev'
    
    $ vim test.txt
    
    $ cat test.txt
    第一行代码,第一次修改
    第二行代码,第二次修改
    分支测试
    master 分支提交
    添加一这行,用于新的分支 feature1,哈哈哈哈
    def func():
            print('正常的开发!')
    另一个小伙伴开发      # bob 添加
    
    $ git add .
    
    
    $ git commit -m '由另一个小伙伴提交'[dev 30e4c14] 由另一个小伙伴提交
     1 file changed, 1 insertion(+)
    
    # 推送到远程
     $ git push origin dev
    Enumerating objects: 5, done.
    Counting objects: 100% (5/5), done.
    Delta compression using up to 4 threads
    Compressing objects: 100% (3/3), done.
    Writing objects: 100% (3/3), 343 bytes | 171.00 KiB/s, done.
    Total 3 (delta 2), reused 0 (delta 0)
    remote: Resolving deltas:   0% (0/2)remote: Resolving deltas:  50% (1/2)remote: Resolving deltas: 100% (2/2)remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
    remote:
    remote: Create a pull request for 'dev' on GitHub by visiting:
    remote:      https://github.com/hj1933/git_test/pull/new/dev
    remote:
    To github.com:hj1933/git_test.git
     * [new branch]      dev -> dev
    
    

    推送成功后,打开 GitHub,选择 dev 分支,发现刚才添加的内容一句推送上来了。

    1. 这是 rose 也对 test.txt 文件做修改,在最后添加一句 自己开发,并且也要推送到远程:
    $ vim test.txt
    
    $ cat test.txt
    第一行代码,第一次修改
    第二行代码,第二次修改
    分支测试
    master 分支提交
    添加一这行,用于新的分支 feature1
    def func():
            print('正常的开发!')
    自己开发
    
    $ git add .
    
    $ git commit -m '自己提交,多人协作'
    [dev ac8a516] 自己提交,多人协作
     2 files changed, 2 insertions(+)
     create mode 100644 .gitignore
    
    

    现在尝试把 dev 分支的修改推送到远程:

    $ git push origin dev
    To github.com:hj1933/git_test.git
     ! [rejected]        dev -> dev (fetch first)
    error: failed to push some refs to 'git@github.com:hj1933/git_test.git'
    hint: Updates were rejected because the remote contains work that you do
    hint: not have locally. This is usually caused by another repository pushing
    hint: to the same ref. You may want to first integrate the remote changes
    hint: (e.g., 'git pull ...') before pushing again.
    hint: See the 'Note about fast-forwards' in 'git push --help' for details.
    
    

    发现推送失败,这是因为 bob 提交了最新的代码,与你的有冲突。解决办法就是把远程仓库抓取最新的代码,再重新提交:

    # 抓取远程最新代码
    $ git pull
    remote: Enumerating objects: 5, done.
    remote: Counting objects: 100% (5/5), done.
    remote: Compressing objects: 100% (1/1), done.
    remote: Total 3 (delta 2), reused 3 (delta 2), pack-reused 0
    Unpacking objects: 100% (3/3), done.
    From github.com:hj1933/git_test
     * [new branch]      dev        -> origin/dev
    There is no tracking information for the current branch.
    Please specify which branch you want to merge with.
    See git-pull(1) for details.
    
        git pull <remote> <branch>
    
    If you wish to set tracking information for this branch you can do so with:
    
        git branch --set-upstream-to=origin/<branch> dev
    
    

    git pull 也失败了,但是 Git 提示我们要设置本地 dev 分支与远程 dev 分支的链接(在错误的最后又提示):

    $ git branch --set-upstream-to=origin/dev dev
    Branch 'dev' set up to track remote branch 'dev' from 'origin'.
    
    # 再重新抓取最新代码,抓取成功
    $ git pull
    Auto-merging test.txt
    CONFLICT (content): Merge conflict in test.txt
    Automatic merge failed; fix conflicts and then commit the result.
    
    

    虽然从远程重新拉取最新的代码,但是冲突还是存在,需要手动去解决,这就和分支冲突解决方式一样:

    # 查看状态,git 告诉我们两个(bob、rose)都修改了 test.txt
    $ git status
    On branch dev
    Your branch and 'origin/dev' have diverged,
    and have 1 and 3 different commits each, respectively.
      (use "git pull" to merge the remote branch into yours)
    
    You have unmerged paths.
      (fix conflicts and run "git commit")
      (use "git merge --abort" to abort the merge)
    
    Unmerged paths:
      (use "git add <file>..." to mark resolution)
    
            both modified:   test.txt
    
    no changes added to commit (use "git add" and/or "git commit -a")
    
    
    $ cat test.txt
    第一行代码,第一次修改
    第二行代码,第二次修改
    分支测试
    master 分支提交
    添加一这行,用于新的分支 feature1,哈哈哈哈
    def func():
            print('正常的开发!')
    <<<<<<< HEAD
    自己开发
    =======
    另一个小伙伴开发
    >>>>>>> 30e4c14cd4ebd80b83689d233e2f37a8bb7b3ccc
    
    

    手动解决冲突后,添加提交再重新推送到远程:

    $ git add .
    
    $ git commit -m '多人协作冲突解决'
    [dev 041befd] 多人协作冲突解决
    
    # 推送到远程
    $ git push origin dev
    Enumerating objects: 11, done.
    Counting objects: 100% (11/11), done.
    Delta compression using up to 4 threads
    Compressing objects: 100% (6/6), done.
    Writing objects: 100% (7/7), 699 bytes | 174.00 KiB/s, done.
    Total 7 (delta 3), reused 0 (delta 0)
    remote: Resolving deltas: 100% (3/3), completed with 1 local object.
    To github.com:hj1933/git_test.git
       30e4c14..041befd  dev -> dev
    
    

    8.3 多人协作模式

    • 先尝试往远程推送 git push origin 分支名
    • 若推送失败,而说明远程比本地更新,则需要重新从远程拉取代码 git pull,拉取时需要指定本地分支与远程分支的链接
    • 若合并时若有冲突则解决冲突,再重新提交并推送到远程,没有远程就能直接推送成功

    8.4 总结

    • 查看远程仓库信息,git remote -v
    • 从远程克隆到本地只有一个 master 分支,没有其他分支
    • 从本地推送分支 git push origin 分支名,若推送失败则从远程拉取最新代码 git pull
    • 在本地创建于远程一直的分支 git checkout -b 分支名
    • 建立本地与远程分支的关联,git branch --set-upstream 分支名 origin、分支名
    • 拉取最新代码后,也要注意查看是否有冲突,解决冲突后再重新推送

    9. 其他

    9.1 使用 GitHub 要注意的事项

    GitHub 是一个免费的代码托管平台,很多开源项目都托管在上面,如:ShadowsocksBootstrap 等等。

    如果想为开源项目共享一份自己的力量,那么可以 fork 一份到自己账号中,然后再 clone 到本地仓库即可修改提交了。若是直接从开源项目仓库中 clone,将无法推送修改,因为没有权限


    Pull Request

    为开源项目做贡献,主要分为以下几步:

    1. 从开源项目仓库 Fork 到自己远程仓库中
    2. 从自己远程仓库 clone 到本地仓库
    3. 创建一个新的分支,修改或添加源码后,pull 到自己远程仓库中
    4. 打开 GitHub,并切换到刚才创建的分支上,然后发起一个 Pull Request,它的意思就是给开源项目发起一个合并代码的请求,若是作者同意则会合并,成功后则会以邮件形式通知你

    9.2 忽略特殊文件 .gitignore

    所谓 .gitignore 文件就是用来配置一些不想被其他用户看到的 特殊文件,将其忽略掉。

    忽略文件的原则是:

    1. 忽略操作系统自动生成的文件,如缩略图等
    2. 忽略编译生成的中间文件、可执行文件,如 Python 文件的 .pyc 文件
    3. 忽略一些带有敏感信息的配置文件,如一个项目的数据库账户、密码等

    我们不需要从头写 .gitignore 文件,Git 以及为我们配置好了各种忽略文件,我们只需再稍作修改即可 https://github.com/github/gitignore

    Tips:

    Windows 上创建一个 .gitignore 文件,需要在文本编辑器中已另存为方式才能创建,否则将要你输入文件名。


    • 在 Git 工作区的根目录下创建一个特殊的.gitignore 文件
    • .gitignore 文件创建后,也要提交到 Git 中
    • 检查 .gitignore 的标准是用 git status 是不是 working directory clean
    • 若要添加一个文件到 Git,发现添加失败,原因可能是被忽略了
    $ git add t2.class
    
    The following paths are ignored by one of your .gitignore files:
    t2.class
    Use -f if you really want to add them.
    
    
    • 强制添加 $ git add -f t2.class
    • 若发现 .gitignore 写的有问题,可以使用以下命令检查:
    $ git check-ignore -v t2.class
    
    # 告诉你 .gitignore 文件第三行忽略了该文件
    .gitignore:3:*.class     t2.class
    
    

    参考文章

  • 相关阅读:
    生活感悟(一)
    DOM数据制作(采用卫星遥感图像数据制作)
    对话框显示前的操作
    sqlHelper中DataReader的关闭问题
    整数的取余运算
    C#中的字符串格式String.Format
    SQL分页查询
    级联删除与更新的例子
    C#中的运算符重载(以重载+为例)
    [高效编程读书笔记]用readonly而不是const
  • 原文地址:https://www.cnblogs.com/midworld/p/10847201.html
Copyright © 2020-2023  润新知