git

Git ABC

Posted on June 14, 2015

本文是6月份在公司的一次git培训文档

一. 关于Git

Git: Linus 的第二个伟大作品, 是Linus用C实现的一个分布式版本控制工具

集中式: 如SVN

分布式: 如Git

优势:

  1. 去中心化
  2. 本地提交
  3. 分支策略

概念

  • Local Structure
    • Workspace 工作区
    • Index/Stage 索引区
    • Local Repo 仓库
  • Objects
    • commit 提交
    • tree 目录树
    • blob 文件
    • tag 里程碑
  • References
    • branch 分支
    • remote 远程
    • HEAD 头指针
  • Actions
    • status
    • add
    • commit
    • checkout
    • reset
    • diff
    • log
    • pull
    • push
    • blame

二. 理解Git

What seems complex from a distance is often quite simple when you look closely enough

1. 本地仓库结构

本地仓库结构

  • 工作区(workspace):本地仓库里除了.git/以外的文件

  • 版本库(Repository):.git/里的所有文件

  • 暂存区(stage or index)一个包含文件索引的目录树,.git/index(二进制文件)


2. 对象和引用的关系

对象关系

  • git对象:.git/objects/ git 对象分为
    • commit(提交)
    • tree(目录)
    • blob(文件)
    • tag(里程碑)

三. 使用Git

1. 本地操作

本地操作

  • git add

    git add 默认只把工作区修改和新增的文件加入index,并不会把在工作区的删除加入index。

    • git rm <path> 在工作区和index区删除该path文件

    • git add -u <path>修改,删除加入index, 但是不包括新增

    • git add -A <path>修改,新增,删除 都加入index

    • git add -p 交互式地询问是否把修改片段添加到index,按照修改片段来区分,不是按照修改文件。

  • git commit

    • -m 添加内联说明消息

    • --amend -m "..." 修改最新的一个提交的说明, commit号会被修改, 但是父提交不会改变

    • -a 对工作区和index区修改和删除文件进行提交(对未入版本库的文件不起作用),跳过git add 不赞成使用

  • git reset

    git reset 的主要作用有2个:

    1)用当前版本库的文件替换index区或者工作区

    2)用指定的历史提交内容,替换版本库,index区,工作区

    • git reset [commit] --<paths> 带有文件名, 用该commit(默认HEAD)的该文件替换当前index中的文件,通常作为git add操作的回滚 [repository->index]

    • git reset [方式] [commit] ,有以下几种方式,方式默认是–mixed:

      • –soft:只把当前分支指向改为commit(默认HEAD), 一个用例是合并本地多步提交 [commit -> repository]

        • git reset --soft HEAD^^ 工作区和index都没改变
        • git commit -m "这个动作合并了3个提交为一个" 因为index内容没被reset,现在直接commit即实现了修改log(reset),但是不改内容的目的
      • –mixed:除了实现soft,还把index区替换为commit指向的目录树 [commit -> repository and index]

      • –hard:除了实现mixed,还把工作区替换为commit指向的目录树的内容一致 [commit -> repository, index, work] 注意index的新文件会被去掉,work的新文件会保留

    • 回滚到上次HEAD的提交: git reset --hard HEAD@{1}

  • git checkout

    • git checkout [commit] -- <paths> 带有path,不会改变HEAD内容。当

      • 省略 commit: 用index的paths文件替换工作区相应文件 [index->work]

      • 带有 commit:用版本库中的paths文件替换index和工作区相应文件 [repository->index and work] 注意index和工作区的新文件不会被更改

  • git diff

    • git diff 工作区和index比较

    • git diff HEAD 工作区和当前分支版本库比较

    • git diff --cached git diff --staged index和版本库比较

    • git diff $start_commit..$end_commit -- path/to/file 2个提交之间diff, 文件可选, 注意文件路径前有空格

2. 回滚

  • git reflog --all HEAD 变化记录
  • git reset --hard HEAD@{1} 回滚到上一次HEAD的指向
  • git push -f 参数 -f 将会强制(force)把本地老代码推送到远端, 以覆盖新代码
  • 回滚后为出现某些机器提交号不一致?

    对于force push后的版本库, 如果另外一台机器代码存在force去掉的代码, 此时此机器pull 代码后, 废旧代码任然可能存在, 这容易造成force更新后, 各个机器commit不一致的情况(所谓的水印不一致)

    解决方案, 其他机器执行git reset --hard origin/master


四. 雕虫小技

Learn more, Do less

  • git clean -fd 删除本地非版本库, 非ignore的文件
  • git cherry-pick 提交号 重放该提交
  • git revert 提交号 创建一个新的commit, 用以undo 指定的提交, 如果能回滚(即指定提交后, 没有其他提交修改这部分代码), 会直接到commit步骤, 如果不能回滚, 将会发送冲突
  • git log <since>..<until> 查看2个引用之间的提交,常用于比较当前分支和远程该分支的提交差异, 输出可以理解为后面一个减去前面一个(后面一个比前面一个多的 commit)

    • 看看是否有提交没有push: git log origin/分支名..HEAD

    • 看看后面一个分支比前一个分支多的commit: git log origin/分支A..分支B

    • 看看是否本地不是最新(应该在remote updaate之后): git log HEAD..origin/分支名

    • 看看2次HEAD变更之间的所有提交log git log HEAD@{1}..HEAD@{0} 注意顺序,调换2个head的位置,无输出

  • git commit --amend -m "..." 修改最新的一个提交的说明, commit号会被修改, 但是父提交不会改变
  • 使用reset –soft 进行合并提交记录
  • git bisect 二分查找法排查bug

五. 其他主题

To be continue

  • git config
  • git stash
  • gitignore
  • git hooks
  • git tag
  • git flow
  • git subtree
  • git commit 规范
  • git 补丁
  • git rebase

六. 抛砖引玉


ToDo