git基本操作

由于学习更偏重点性记录或自己使用角度,深入度可能不足,详细见https://gitee.com/progit/index.html

安装及配置Git · Pro Git 第二版 简体中文

起步

  • 版本控制 : 记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统

  • 本地版本控制

    • eg : RCS工作原理是在硬盘上保存补丁集(补丁是指文件修订前后的变化);通过应用所有的补丁,可以重新计算出各个版本的文件内容。
  • 集中版本控制 : 不同系统上的开发者协同工作

    • 有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新
    • 优点 : 每个人都可以在一定程度上看到项目中的其他人正在做些什么。 而管理员也可以轻松掌控每个开发者的权限
    • 缺点 : 中央服务器的单点故障,只有单独快照
  • 分布式版本控制

    • 客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来
  • git基础

    • Git 是把数据看作是对小型文件系统的一组快照。 每次提交更新,或在 Git 中保存项目状态时,主要对当时的全部文件制作一个快照并保存这个快照的索引。 如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个 快照流
    • Git 中的绝大多数操作都只需要访问本地文件和资源,一般不需要来自网络上其它计算机的信息
    • Git 中所有数据在存储前都计算校验和,然后以校验和来引用。 这意味着不可能在 Git 不知情时更改任何文件内容或目录内容
      • 计算校验和的机制叫做 SHA-1 散列(hash,哈希)。 这是一个由 40 个十六进制字符(0-9 和 a-f)组成字符串,基于 Git 中文件的内容或目录结构计算出来
      • 实际上,Git 数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名
    • 执行的 Git 操作,几乎只往 Git 数据库中增加数据
    • 三种状态
      • 已提交表示数据已经安全的保存在本地数据库中
      • 已修改表示修改了文件,但还没保存到数据库中
      • 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中
    • 由此 三个工作区
      • Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方(github)
      • 工作目录是对项目的某个版本独立提取出来的内容(本地仓库)
      • 暂存区域是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。称作索引
    • 基本Git工作流程
      • 在工作目录中修改文件
      • 暂存文件,将文件的快照放入暂存区域
      • 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录

Git基础

  • 获取Git仓库

    • 在现有目录中初始化仓库 git init

      • 在已经存在 文件 的文件夹中初始化

        • $ git add *.c
          $ git commit -m 'initial project version'
          
    • 克隆现有仓库

      • git clone [url]
  • 记录每次更新到仓库

    工作目录下的每一个文件都不外乎这两种状态:已跟踪(git add)or未跟踪

    已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后,它们的状态可能处于未修改,已修改或已放入暂存区

    工作目录中除已跟踪文件以外的所有其它文件都属于未跟踪文件,它们既不存在于上次快照的记录中,也没有放入暂存区。

    初次克隆(git clone)某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态

    • 检查当前文件状态 git status

      • 目录干净 :所有已跟踪文件在上次提交后都未被更改过
      • Untracked files 下面: 未跟踪的文件意味着 Git 在之前的快照(提交)中没有这些文件
      • Changes to be committed 这行下面 : 是已暂存状态。 如果此时提交,那么该文件此时此刻的版本将被留存在历史记录中
      • Changes not staged for commit 这行下 : 已跟踪文件的内容发生了变化,但还没有放到暂存区。 要暂存这次更新,需要运行 git add 命令
    • 跟踪新文件 git add newfile

      • git add理解 :添加内容到下一次提交中
    • 忽略文件 :总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表

    • 查看已暂存和未暂存的修改 git diff

      • git diff : 修改之后还没有暂存起来的变化内容
      • git diff --cached : 查看已暂存的将要添加到下次提交里的内容
    • 提交更新

      • 都已暂存起来了, 然后再运行提交命令 git commit
    • 跳过使用暂存区域

      • git commit 加上 -a 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤

        $ git commit -a -m 'added new benchmarks'
        
    • 移除文件 手工删除 : 运行 git status 会在 Changes not staged for commit 部分 git rm命令 : 从已跟踪文件清单中移除,并从工作目录中删除指定的文件,就不会出现在未跟踪文件清单中

      • 也可以使用 glob 模式
      • 强制删除 : git rm -f file
      • 使用 --cached 选项:把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中
    • 移动文件 : 不显示跟踪文件移动操作 重命名$ git mv file_from file_to

  • 查看提交历史

    • git log :会按提交时间列出所有的更新,最近的更新排在最上面

      作者指的是实际作出修改的人 提交者指的是最后将此工作成果提交到仓库的人 常用的选项是 -p,用来显示每次提交的内容差异。 也可以加上 -2 来仅显示最近两次提交

    • 其他常见参数,具体学习 查看提交历史 · Pro Git 第二版 简体中文

    • git log --oneline --decorate --graph --all ,它会输出你的提交历史、各个分支的指向以及项目的分支分叉情况

  • 撤销操作

    • 提交信息写错,可以运行带有 --amend 选项的提交命令尝试重新提交

      • $ git commit -m 'initial commit'
        $ git add forgotten_file
        $ git commit --amend
        
    • 取消暂存区文件,将暂存区文件变为暂存状态

      • git reset HEAD <file>...
        eg: $ git reset HEAD CONTRIBUTING.md
        
    • 取消本次修改,还原成上次提交样子

      • use "git checkout -- <file>..." to discard changes in working directory
        
        eg :$ git checkout -- CONTRIBUTING.md
        
  • 远程仓库的使用

    • 远程仓库 : 托管在因特网或其他网络中的你的项目的版本库

    • 查看远程仓库 :运行 git remote 命令。 它会列出你指定的每一个远程服务器的简写

      • 可以指定选项 -v,显示需要读写远程仓库使用的 Git 保存的简写与对应的 URL
      • git remote show [remote-name] : 查看某一个远程仓库的更多信息
    • 添加远程仓库 :运行 git remote add <remote-name> <url> 添加一个新的远程 Git 仓库,同时指定一个轻松引用的简写

    • 从远程仓库中抓取与拉取

      • git fetch [remote-name] : 访问远程仓库,从中拉取所有你还没有的数据,拥有那个远程仓库中所有分支的引用,可以随时合并或查看
      • 使用 clone 命令克隆了一个仓库,命令会自动将其添加为远程仓库并默认以 ``origin 为简写。git fetch origin` 会抓取克隆(或上一次抓取)后新推送的所有工作
      • 注意 git fetch 命令会将数据拉取到你的本地仓库 - 它并不会自动合并或修改你当前的工作
      • git pull 区分如下 :更简单
        • 如果你有一个分支设置为跟踪一个远程分支,可以使用 git pull 命令来自动的抓取然后合并远程分支到当前分支
        • 默认情况下,git clone 命令会自动设置本地 master 分支跟踪克隆的远程仓库的 master 分支(或不管是什么名字的默认分支)
        • git pull 通常会从最初克隆的服务器上抓取数据并自动尝试合并到当前所在的分支
    • 推送到远程仓库

      • git push [remote-name] [branch-name]。 当你想要将 master 分支推送到 origin 服务器时(再次说明,克隆时通常会自动帮你设置好那两个名字
    • 远程仓库的移除与重命名

      • git remote rename 去修改一个远程仓库的简写名

        • $ git remote rename pb paul
          
        • 修改你的远程分支名字。 那些过去引用 pb/master 的现在会引用 paul/master

      • 移除一个远程仓库 - 你已经从服务器上搬走了或不再想使用某一个特定的镜像了,又或者某一个贡献者不再贡献了 - 可以使用 git remote rm

  • 打标签

    • Git 可以给历史中的某一个提交打上标签,以示重要。 有代表性的是人们会使用这个功能来标记发布结点(v1.0 等等)

    • git tag : 列出已有的标签是非常简单直观的

    • git show 命令可以看到标签信息与对应的提交信息 eg : git show v1.4

    • 创建标签

      • 轻量标签(lightweight):像一个不会改变的分支 - 它只是一个特定提交的引用
      • 附注标签(annotated):存储在 Git 数据库中的一个完整对象及拥有很多信息
      • 如果你只是想用一个临时的标签,或者因为某些原因不想要保存那些信息,轻量标签也是可用的
    • 附注标签

      • 运行 tag 命令时指定 -a 选项,-m 选项指定了一条将会存储在标签中的信息

        $ git tag -a v1.4 -m 'my version 1.4'
        
    • 轻量标签

      • 创建轻量标签,不需要使用 -a-m 选项,只需要提供标签名字

        $ git tag v1.4-lw
        
    • 后期打标签

      • 查看过去提交历史,eg :git tag -a <版本> <历史版本hash>
    • 共享标签

      • git push 命令并不会传送标签到远程仓库服务器上,运行 git push origin [tagname]

      • 一次性推送很多标签,也可以使用带有 --tags 选项的 git push 命令。 这将会把所有不在远程仓库服务器上的标签全部传送到那里

         git push origin --tags
        
    • 检出标签

      • 如果你想要工作目录与仓库中特定的标签版本完全一样,可以使用 git checkout -b [branchname] [tagname] 在特定的标签上创建一个新分支
      • 但分支被提交会改变,要注意

Git分支

  • 分支简介

    • Git 保存的不是文件的变化,而是一系列不同时刻的文件快照

    • Git 会保存一个提交对象(commit object),我们可以想到——该提交对象会包含一个指向暂存内容快照的指针

    • 分支创建

      • 为你创建了一个可以移动的新的指针,使用 git branch

        $ git branch testing
        
      • HEAD 指向当前所在的分支,而且HEAD 分支随着提交操作自动向前移动

    • 分支切换

      • 切换到一个已存在的分支,你需要使用 git checkout

        $ git checkout testing
        
      • 分支切换会改变你工作目录中的文件

        在切换分支时,一定要注意你工作目录里的文件会被改变。 如果是切换到一个较旧的分支,你的工作目录会恢复到该分支最后一次提交时的样子。

        如果 Git 不能干净利落地完成这个任务,它将禁止切换分支

  • 分支新建与合并merge

    • 新建分支

      • 运行一个带有 -b 参数的 git checkout新建并同时切换到那个分支上
    • 合并分支

      • 如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候,只会简单的将指针向前推进(指针右移)

        $ git checkout master
        $ git merge hotfix
        Updating f42c576..3a0874c
        Fast-forward
         index.html | 2 ++
         1 file changed, 2 insertions(+)
        

        此时 hotfix 与 master 指向同一快照版本C4

      • 从一个更早的地方开始分叉开来,进行合并

        $ git checkout master
        Switched to branch 'master'
        $ git merge iss53
        Merge made by the 'recursive' strategy.
        index.html |    1 +
        1 file changed, 1 insertion(+)
        

        一次典型合并中所用到的三个快照

    • 删除分支

      • 使用带 -d 选项的 git branch 命令
      • 如果分支没有被合并,Git 会阻止你删除该分支,以防止丢失未合并的提交。你可以使用 git branch -D <branch_name> 强制删除分支,但这样做会丢失未合并的提交.
    • 遇到冲突时的分支合并

      • 如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,Git 就没法干净的合并它们
      • git status 命令来查看那些因包含合并冲突而处于未合并(unmerged)状态的文件,进行修改
  • 分支管理

    • git branch 命令不只是可以创建与删除分支。 如果不加任何参数运行它,会得到当前所有分支的一个列表
    • 查看每一个分支的最后一次提交,运行 git branch -v
    • 查看所有包含未合并工作的分支,可以运行 git branch --no-merged
    • 查看哪些分支已经合并到当前分支,可以运行 git branch --merged
  • 分支开发工作流

    • 长期分支:有几个固定长期分支
      • 比如只在 master 分支上保留完全稳定的代码——已经发布代码。 还有一些名为 develop 或者 next 的平行分支,被用来做后续开发——这些分支不必保持绝对稳定,但是一旦达到稳定状态,它们就可以被合并入 master
    • 特性分支
      • 创建一些新分支创作,进行合并,之后删除。
  • 远程分支

    • git push (远程仓库名)/(分支名) 就会改变此远程仓库的分支位置

    • 远程分支(remote branch)是对远程仓库中的分支的索引,只有在 Git 进行网络交互时才会更新,用 (远程仓库名)/(分支名) 的形式表示远程分支

    • 如果你在本地 master 分支做了些改动,与此同时,其他人向 git.ourcompany.com 推送了他们的更新,那么服务器上的 master 分支就会向前推进,而于此同时,你在本地的提交历史正朝向不同方向发展。不过只要你不和服务器通讯,你的 origin/master 指针仍然保持原位不会移动

      • 运行 git fetch origin 来同步远程服务器上的数据到本地

    • 推送本地分支

      1. 需要和他人一起开发此分支,可以运行 git push (远程仓库名) (分支名),完整写法git push (远程仓库名) (本地分支名):(远程分支名)

      2. 值得注意的是,在 fetch 操作下载好新的远程分支之后,你仍然无法在本地编辑该远程仓库中的分支

      3. 要把该远程分支的内容合并到当前分支,可以运行 git merge (远程仓库名)/(远程分支名) 或自己重新一个分支

    • 跟踪远程分支

      • 从远程分支 checkout 出来的本地分支,称为 跟踪分支 (tracking branch)。
      • 跟踪分支是一种和某个远程分支有直接联系的本地分支。
      • 跟踪分支里输入 git push,Git 会自行推断应该向哪个服务器的哪个分支推送数据。同样,在这些分支里运行 git pull 会获取所有远程索引,并把它们的数据都合并到本地分支中来。而git clone会自动创建跟踪
      • git checkout -b [分支名] [远程名]/[分支名] 可以创建跟踪远程分支
    • 删除远程分支

      • git push [远程名] :[分支名]可以理解为 : 在这里提取空白然后把它变成[远程分支]”。
  • 变基rebase

    • 在 Git 中整合来自不同分支的修改主要有两种方法:merge 以及 rebase

    • 你可以提取在 C4 中引入的补丁和修改,然后在 C3 的基础上再应用一次。 在 Git 中,这种操作就叫做 变基

    • 具体操作如下

      • $ git checkout experiment
        $ git rebase master
        First, rewinding head to replay your work on top of it...
        Applying: added staged command
        

        $ git checkout master
        $ git merge experiment
        

        理解如下

      • 先找到这两个分支(即当前分支 experiment、变基操作的目标基底分支 master)的最近共同祖先 C2对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件,将当前分支指向目标基底 C3, 最后以此将之前另存为临时文件的修改依序应用。

    • 更高级的例子

      • ==想要将 client 中的修改合并到主分支并发布,但暂时并不想合并 server中的修改==,可以使用 git rebase 命令的 --onto 选项,选中在 client 分支里但不在 server 分支里的修改(即 C8C9),将它们在 master 分支上重演

      $ git rebase --onto master server client
      

      理解方式:取出 client 分支,找出处于 client 分支和 server 分支的共同祖先之后的修改,然后把它们在 master 分支上重演一遍

      快进合并master分支

      $ git checkout master
      $ git merge client
      

    • 变基的风险,遵守一条准则

      • 不要对在 你的仓库外有副本 的分支执行变基
      • 出现风险,解决方法之后学,现在还看不太懂
    • 变基 vs. 合并 具体因人而异

      • 合并:记录实际发生过什么
      • 变基:项目过程中发生的故事
      • 核心观点只对尚未 推送或分享 给别人的本地修改执行变基操作清理历史,从不对已推送至别处的提交执行变基操作

    以上基本操作使用学习完毕