说明

  • ”–” 代表全称
  • ”-“ 代表缩写
  • [-u –update]
  • :是两个分支(push fetch pull)
  • /是主机名和分支(merge、 –set-upstream-to)
  • 写文件名字的地方不写,代表全部文件
  • Git 保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照
  • 裸双破折号:分离参数,如命令和子命令
  • “^”代表父提交,当一个提交有多个父提交时,可以通过在”^”后面跟上一个数字,表示第几个父提交,”^”相当于”^1”.
  • ~[n]相当于连续的[n]个”^”.

Git 基本概念

  • 对象库(object store) 和 索引(index)

  • 块(blob):文件的每一个版本表示一个blob。一个blob被视为一个黑盒。一个blob保存一个文件的数据,但不包含任何关于这个文件的元数据,甚至连文件名也没有。
  • 它什么也不引用而且只被树对象引用。

  • 目录树(tree):一个目录树对象代表一层目录信息,它记录blob标识符、路径名和一个目录里所有文件的一些元数据。
  • 树对象指向若干个 blob 对象,也可能指向其他树对象。

  • 一个提交(commit)对象保存版本库中每一次变化的元数据.
  • 一个提交对象指向一个树对象,并且这个树对象是由提交对象引入版本库的。
  • 每个提交对象指回它的一个或多个父提交。最初的提交是没有父提交的,大多数提交都有一个父提交。一个提交也可以引用多个父提交。

  • 一个标签对象分配一个任意的且人类可读的名字给一个特定对象,通常是一个提交对象。最多指向一个提交对象。

  • 索引
  • 索引是一个临时的、动态的二进制文件,它描述整个版本库的目录结构。更具体地说,索引捕获项目在某个时刻的整体结构的一个版本。米昂木的状态可以用一个提交和一颗目录树表示,它可以来自项目历史中的任意时刻,或者它可以是你正在开发的未来状态。

  • Git的关键特色之一就是它允许你用有条理的、定义好的步骤来改变索引的内容。索引式的开发的推进与提交的变更之间能够分离开来。

  • 索引支持一个由你主导的从复杂的版本库状态到一个可推测的更好状态的逐步过渡。

  • git cat-file -p 3bd8e512dba79e4c8300dd08aeb37f8e728b8dad
    • 可以在任何时间使用散列值把它从对象库里取出来
    • .git/object
  • 通过对象的唯一前缀查找散列值 git rev-parse 3b18e512d –> 3b18e512dba79e4c8300dd08aeb37f8e728b8dad

  • sha1 160位 20字节 对应 16进制40位
  • 在前两位后加”/” 表示目录(还是一个字节8bit)。共256个
二进制 八进制 十六进制
1bit二进制 3bit二进制 4bit二进制

可寻址内容名称

Git对象库 被 组织及实现成一个内容寻址的存储系统。具体而言,对象库中的每个对象都有一个唯一的名称,这个名称是 向对象的内容应用 SHA1 得到的 SHA1 散列值。因为一个对象的完整内容决定了这个散列值,并且认为这个散列值能有效并唯一地对应特定的内容,所以 SHA1 散列值用来做对象数据库中对象的名字和索引是完全充分的。

  • Git 的对象库基于对象内容的三列计算的值,而不是基于用户原始文件布局的文件名或目录名设置。

  • Git把文件的每一个版本存储为blob文件,并不做diff,只有在pack的时候,才会有算法按一定的策略计算delta。

Pro Git

  • 三种状态
    • 已提交(committed)
    • 已修改(modified)
    • 已暂存(staged)
  • 三个工作区
    • repository:Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。永久性存储。
    • working area:工作目录是对项目的某个版本独立提取出来的内容。
    • stage:暂存区域是一个文件,保存了下次将提交的文件列表信息。

安装

  • sudo apt-get install git
  • git –version

初始化、配置

  • git init || git init [direction] || git clone -o [自定义远程主机名] [版本库的网址] [本地目录名]
  • git config --system/--global/--local user.name/user.email/core.editor "值"
  • core.editor "sublime -w"
    • 输出完成后必须关闭 sublime text,否则 Git 无法完成提交
  • /etc/gitconfig ; ~/.gitconfig ; .git/config
  • git config --list
  • git config --local --unset user.name (删除配置项)
  • git config --global color.ui auto
  • git config --global push.default simple
    • matching:git 将推送和远程同名的所有本地分支。
    • simple:只推送当前分支到远程关联的同名分支,即 ‘git push’ 推送当前分支。
  • git config user.name/user.email //是提交者
  • alias
    • git config alias.ad "add"
  • commit 模板
    • git config --global commit.template $HOME/.gitmessage.txt
    • git commit
  • 常用配置
    1. user.name
    2. user.email
    3. push.default
    4. core.editor
    5. color.ui
    6. push.template

clone

  • git clone [本地仓库] [新的仓库]

  • 查看区别
  • git diff -r [基本库][新库]

Index/Stage

  • git add [file] || git add [direction] || git add .

  • git add -u [<path>](只针对被跟踪的文件,--update)

  • git mv [旧名][新名]

  • git rm [file]

    Remove files from the working tree(tracked files in working tree) and from the index

  • git rm --cached [file]

    删除暂存区或分支上的文件, 但本地又需要使用, 只是不希望这个文件被版本控制,然后在.gitignore文件里添加上该文件,重新提交即可

  • git rm -r [目录]

    (用于递归删除目录recursive,一般要配合-f)

  • 如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项 -f(译注:即 force 的首字母)。这是一种安全特性,用于防止误删还没有添加到快照的数据,这样的数据不能被 Git 恢复

  • git checkout -- [file]

Repository

  • git commit -m [message][文件1] [文件2][...]

  • git commit -m [message]

    这里不写文件说明是所有暂存区中的文件及目录

  • git commit -am [message]

  • git commit -v [文件1][文件2] [文件3][...]

    并不提交,提交时显示所有diff信息

  • git commit --amend [文件1][文件2] [文件3][...]

  • git commit --amend -m [message][文件1] [文件2][文件3] [...]

  • git reset --hard (Resets the index and working tree)

  • git reset --soft 移动到暂存区(Does not touch the index file or the working tree at all )

  • git reset --mixed 移动到工作区(Resets the index but not the working tree)

  • git reset 是reset to [commit],commit 之后新的修改分流到 –hard/–soft/–mixed

  • git reset 不能指定文件

  • 版本回退到[commit]
    • –mixed 将这次提交之后的所有变更都移动到工作区
    • –soft 将这次提交之后的所有变更都移动到暂存区
    • –hard 重置暂存区
    • –hard / –soft 不能用于具体的文件
    • git reset –mixed HEAD A.txt 不可以
    • git reset HEAD A.txt 正确,将暂存区文件恢复到工作区
  • git commit -m [message] –author “姓名 <邮箱>" //提交者

Stash(储藏)

  • 为什么有的命令要有–,而有的不需要,什么逻辑?

  • git stash
  • git stash drop stash@{number}
  • git stash list
  • git stash apply stash@{number}
  • git stash pop stash@{number}

Branch

  • git branch [branch name][基于哪个branch]

  • git branch [branch name][基于哪个branch]

  • git checkout -b [branch name][基于哪个branch]

  • git fetch origin dev

  • git branch dev origin/dev

  • git branch -d [branch name]

  • git branch -m [新branch name]

  • git checkout [branch name]

  • git checkout -

  • git branch

  • git branch -r

  • git branch -a

  • git branch --list

  • git branch --set-upstream-to=[远程主机]/[远程分支][本地分支]

  • git branch -u [远程主机]/[远程分支][本地分支]

  • 如果没有写[本地分支],则默认当前分支

  • git branch -vv 查看本地branch与远程branch的关联,如果没有的话,要用git branch –set-upstream-to=origin/master master来设定

  • git push origin --delete [远程主机]/[远程分支] (删除远程分支),没有简写成-d的写法

  • git push origin :master

    • 如果忽略本地分支,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支。
  • git branch -dr [remote/branch]

  • git merge [branch name] (合并某个分支到当前分支)

  • git merge [远程主机名]/[远程分支名] (将远程主机分支合并到当前分支)

    • 还是先pull/fetch下来,再merge的好
  • git cherry-pick [commit]

    选择一个commit,合并进当前分支

  • git fetch 然后运行 git checkout origin/master

  • git checkout origin/master

    切换到远程主机的master分支

  • [如果没有fetch,git checkout origin/master 还是原来就的内容]

  • 如果本地副本没有修改,远程已经更新了,git fetch 后才会显示“Your branch is behind ‘origin/master’ by 1 commit, and can be fast-forwarded.”

  • 匿名分支

  • git checkout [commit] //进入匿名分支

  • 状态:You are in ‘detached HEAD’ state. ……

  • 在匿名分支所做的任何操作,如果没有在这个匿名分支存在的时候创建基于此分支的费匿名分支,一旦离开此匿名分支,所做的工作就永远找不回来了

  • 匿名分支
    • git checkout [commit] //进入匿名分支
    • 状态:You are in ‘detached HEAD’ state. ……
    • 在匿名分支所做的任何操作,如果没有在这个匿名分支存在的时候创建基于此分支的费匿名分支,一旦离开此匿名分支,所做的工作就永远找不回来了
  • how to delete all commit history in github?

    • git checkout –orphan <branch>

      • 假如你的某个分支上,积累了无数次的提交,你也懒得去打理,打印出的log也让你无力吐槽,那么这个命令将是你的神器,它会基于当前所在分支新建一个赤裸裸的分支,没有任何的提交历史,但是当前分支的内容一一俱全。新建的分支,严格意义上说,还不是一个分支,因为HEAD指向的引用中没有commit值,只有在进行一次提交后,它才算得上真正的分支。
1.Checkout   git checkout --orphan latest_branch

2. Add all the files   git add -A

3. Commit the changes   git commit -am "commit message"

4. Delete the branch   git branch -D master

5.Rename the current branch to master   git branch -m master

6.Finally, force update your repository   git push -f origin master

Tag

  • git tag [tag name] (轻量标签(lightweight))
  • git tag [tag name] [commit number]

  • git tag -a [tag name] (附注标签(annotated))
  • 注标签是存储在 Git 数据库中的一个完整对象。它们是可以被校验的;其中包含打标签者的名字、电子邮件地址、日期时间;还有一个标签信息;并且可以使用 GNU Privacy Guard(GPG)签名与验证。通常建议创建附注标签,这样你可以拥有以上所有信息;但是如果你只是想用一个临时的标签,或者因为某些原因不想要保存那些信息,轻量标签也是可用的。

  • git tag -a [tag name] -m [message]

  • git tag -d [tag name]

  • git push origin :refs/tags/tagname

  • git push [远程主机名] [tagname] (推送某个标签到远程)
  • git push [远程主机名] –tags (推送全部标签至远程主机)

  • git push –all origin –tags
    • –all 推送本的所有分支到远程
  • 如果远程主机的版本比本地版本更新,推送时Git会报错,要求在本地做git pull合并差异,然后再推送到远程主机。这时,如果一定要推送,可以使用 –force 选项。
    • git push –force origin
    • 结果导致远程主机上更新的版本被覆盖。
  • git tag
  • git show [tag name]

  • git status
  • git log
  • git log –pretty=oneline

  • git log –decorate //Print out the ref names of any commits that are shown.

  • git log -次数
  • git log –graph (如果你只是浏览一个单独分支的历史,那么这个功能是没有用的)
  • git shortlog (汇总git日志输出)
    • -s 参数省略每次 commit 的注释,仅仅返回一个简单的统计。
    • -n 参数按照 commit 数量从多到少的顺利对用户进行排序

      git log -p [file] (显示指定文件相关的每一次diff, –patch)

  • git log –stat (每次提交的简略的统计信息)

  • git log –pretty=format:”%h - %an %cn”
    • –since/–after
    • –since=2.weeks
    • –since=”2008-01-15”
    • –until/–before –author 作者
    • –committer 提交者
    • –grep 含指定关键字的提交
      • –all-match:Limit the commits output to ones that match all given –grep, instead of ones that match at least one.
      • -i / –regexp-ignore-case:不区分大小写
    • 尽量写成Git的时间格式,date -R,
    • 如果要得到同时满足这两个选项搜索条件的提交,就必须用 –all-match 选项。否则,满足任意一个条件的提交都会被匹配出来
  • git log –oneline –decorate –graph

  • git diff [文件] (工作区和暂存区)
  • git diff (工作区和暂存区)

  • git diff –cached、–stage [文件] (暂存区和版本库) git diff –cached、–stage (暂存区和版本库)

  • git diff [commit] [文件]

  • git diff [commit]

  • git diff [commit1] [commit2]

  • git reflog [文件] (显示当前分支的最近几次提交)
  • git reflog (显示的是所有head移动的信息。记住,它是在本地的,而不是你仓库的一部分,不会包含在推送(push)和合并中(merge))

  • git diff [branch1] [branch2]

  • git blame [文件] (谁、什么时间修改了文件)

repository vs remote

  • FETCH_HEAD指的是:某个branch在服务器上的最新状态。
  • 如果没有显示地指定远程分支,则远程分支的master将作为默认的FETCH_HEAD。 如:git fetch origin或者git fetch origin master
  • 如果指定了远程分支,则将这个远程分支作为FETCG_HEAD。
  • ./git/refs 里面有三个文件夹:heads、remotes、tags。heads 和 remotes 分别记录的就是本地和远程不同仓库的最新 commit id, fetch 改变的是 remotes 里面相应分支的 commit id。

  • git remote add [远程主机名] [url]

  • -u:–set-upstream

  • git push -u [远程主机名] [本地分支]:[远程分支]

  • git pull <远程主机名> <远程分支名>:<本地分支名>
  • 如果你有一个分支设置为跟踪一个远程分支(阅读下一节与 Git 分支 了解更多信息),可以使用 git pull 命令来自动的抓取然后合并远程分支到当前分支。
  • git pull origin dev

  • git remote remove [远程主机名]
  • git remote rename [旧名字] [新名字]
  • git remote set-url [远程主机名] [url]

  • git remote
  • git remote -v

  • git remote show [远程主机名]

  • 同时提交代码至gitee、github
    git remote add origin git@gitee.com:name/remote.git
    git remote set-url --add origin git@github.com:name/remote.git
    git remote -v
    发现后添加的只有一个fetch,后添加的只有push
    
  • git pull 等价操作

    git fetch origin git merge origin/master(假设当前分支为master,将origin/master合并到当前分支)

  • git pull –rebase(不会产生merge提交)
    • 如果是有冲突的话,先解决冲突
    • git add [有冲突的文件]
    • git rebase –continue
    • git status
  • git pull –rebase origin dev:dev(这种只能本地还没有dev分支的时候用,类比git pull origin dev:dev)
  • git pull –rebase origin dev(类比git pull origin dev)

  • –rebase VS merge ,是同类词

  • git merge brnach-b VS git rebase branch-b

rebase(衍合,变基)

  1. 用来替代 merge
  2. 用来修改 commit 历史

    • 改变最近一次提交
      • git commit –amend
      • 这个提交的时间并没有修改
      • 修改时间的方法:
        • git commit –amend –date=”$(date -R)”
        • date -r
        • git commit -amend –date=”上边那条命令的输出”
    • 修改多个提交说明(必须通过告诉命令衍合到哪次提交,来指明你需要重写的提交的回溯深度。)
      • 指明你想要修改的提交的父提交

        git rebase -i HEAD~3
        将需要修改的commit内容前的 pick 改成 edit/e
        :x
        git commit –amend
        git rebase –continue(这里会提示有多少已经done,还有多少remaining)
        {git commit –amend
        git rebase –continue}
        共循环3次

* 重排提交(彻底重排或删除提交)
    * 重排:重新排序commit,直接重新排序即可
    * 删除:直接删除(dd)即可

* 压制(Squashing)提交:指定"squash",Git 会同时应用那个变更和它之前的那一个变更并将提交说明归并。
    * 两个合并,合并三个的话就要写两个s

    * 拆分提交:就是撤销一次提交,然后多次部分地暂存或提交直到结束。 > git rebase -i HEAD~3     > 把需要多次提交的改为 edit    > git reset HEAD^    > modify commit     > modify commit     > ...    > git rebase --continue   

永远不要衍合那些已经推送到公共仓库的更新。

SSH

  1. cd ~/.ssh
  2. ssh-keygen -t rsa -C “ur email”
  3. ssh-add id_rsa
  4. ssh -T git@github.com

Describe

  • git describe 显示离当前提交最近的标签
  • git describe –tags
    • 2:表示自打tag tag1 以来有2次提交(commit);g026498b:g 为git的缩写,

show

  • git show [commit code] //不写commit则默认最近的那次,即HEAD
  • ??? git show-branch –more=2

  • git show –name-only [commit] //这次提交所涉及的文件的名子

思考

  • 工作流程
    • git branch
    • git stash list
    • git status
  • 删除工作区修改的两种方式
    • git checkout – [file]
    • git stash ; git stash drop stash@{0}
  • 删除index修改的两种方式
    • git reset –hard/–mixed HEAD git stash ; git stash drop stash@{0}
  • git pull origin dev:dev 并不会和upstream建立联系,需要使用 
    • git push -u origin master(本地):master(远程) 或 
    • git branch –set-upstream-to=origin/master master(本地)
  • 重命名原理

    mv old.md new.md
    git rm old.me
    git add new.md

  • git rebase -i HEAD~11 (一共11条提交)

    fatal: Needed a single revision upstream HEAD~11

  • master 是什么
    • Anything in the master branch is deployabl
    • 禁止直接提交到 master 分
  • 稳定分
    • master 是稳定分
    • 其他分支 都是不稳定分
  • 在分支上做什
    • 一个分支做且只做一件事 (包括但不限于功能开发 修复Bug 整合 发布 紧急修复 等
    • 如果要做多件事,开多个分
    • 尽量避免多个人在同一分支上做
  • 分支生命周
    • 如果做完一件事,分支的生命周期就结束了,要删掉这个分
    • 分支的生命周期,越短越好,不要长期持有一个分
  • 从哪里来,到哪里
    • 分支的上游只能是 maste
    • 分支的下游只能是 releas
  • 如何上
    • 测试的版本就是上线的版本,上线的版本必须经过测试

git log pretty

选项 说明 %H 提交对象(commit)的完整哈希字串 %h 提交对象的简短哈希字串

%T 树对象(tree)的完整哈希字串 %t 树对象的简短哈希字串

%P 父对象(parent)的完整哈希字串 %p 父对象的简短哈希字串

%an 作者(author)的名字 %ae 作者的电子邮件地址

%ad 作者修订日期(可以用 –date= 选项定制格式) %ar 作者修订日期,按多久以前的方式显示

%cn 提交者(committer)的名字 %ce 提交者的电子邮件地址

%cd 提交日期 %cr 提交日期,按多久以前的方式显示

%s 提交说明

-p 按补丁格式显示每个更新之间的差异。

–stat 显示每次更新的文件修改统计信息。

–shortstat 只显示 –stat 中最后的行数修改添加移除统计。

–name-only 仅在提交信息后显示已修改的文件清单。

–name-status 显示新增、修改、删除的文件清单。

–abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符。

–relative-date 使用较短的相对时间显示(比如,“2 weeks ago”)。

–graph 显示 ASCII 图形表示的分支合并历史。

–pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。

-(n) 仅显示最近的 n 条提交

–since, –after 仅显示指定时间之后的提交。

–until, –before 仅显示指定时间之前的提交。

–author 仅显示指定作者相关的提交。

–committer 仅显示指定提交者相关的提交。

–grep 仅显示含指定关键字的提交

-S 仅显示添加或移除了某个关键字的提交

权限管理

在Git服务管理工具这个领域,主要有三种流行的方案,它们分别是

  1. Gitosis - 轻量级, 开源项目,使用SSH公钥认证,只能做到库级的权限控制。目前项目已经停止开发,不再维护。
  2. Gitolite - 轻量级,开源项目,使用SSH公钥认证,能做到分支级的权限控制。
  3. Git + Repo + Gerrit - 超级重量级,集版本控制,库管理和代码审核为一身。可管理大型及超大型项目。