Git 工作原理及常用命令

1、git的工作空间

git状态流转

  • workspace:工作区,文件改动的区域。
  • staging area:暂存区,执行 git add 命令后,保存修改的区域,存放在 .git 目录下的 index 文件中。
  • local repository:本地仓库,存放在 .git 目录下,管理本地仓库。
  • remote repository:远程仓库。

2、git中文件的几种状态

  

  • untracked:在 woking directory 中新创建了一个文件,这个文件的状态就是untracked。
  • tracked:git 库上已存在的文件,就是 tracked,对于此类文件又可以做如下区分
    • unmodifed:从远程库下载了此文件,但是还没有修改。
    • modifed:修改了,还没有执行 git add 命令。
    • staged:执行了git add命令后,此文件的修改已经被缓存到 staged area。
    • committed:执行了 git commit 命令,此文件的修改被保存到本地库。

3、git 工作原理

记录快照,非差异比较

git 只关心文件数据的整体是否发生变化,而不关注文件的具体内容差异,它会把每次提交的文件全部内容都会记录下来,若文件没有变化,则不会再次保存,只会对上次保存的快照做一个索引链接。每个用来表示项目历史信息的文件,都是对其包含的内容做一个 sha-1 hash 的 40 个字符来索引的。

以某一次提交以下内容为例:

1
2
3
4
5
6
|-- README
`-- lib
|-- inc
| `-- tricks.rb
`-- mylib.rb
2 directories, 3 files

在 git 仓库中保存的结构如下所示:

objects-example

每个目录都创建了 tree对象 (包括根目录),每个文件都创建了一个对应的 blob对象 。最后有一个 commit对象 来指向根 tree 对象, 这样我们就可以追踪项目每一项提交内容.

  • 一个 blob 用来存储文件的内容,它只是一块二进制数据,文件名是对内容做的 sha-1 hash,如果一个目录下有多个相同内容的文件,则他们会指向同一个 blob 对象。
  • 一个 tree 对象有一串指向 blob 对象或是其它 tree 对象的指针,它用来表示内容之间的目录层次关系,tree 对象也是以其内容的 sha-1 hash 来命名的。
  • “commit对象” 指向一个 “tree对象”,并且带有相关的描述信息。它的修改信息都是通过与父提交的内容比较得出。值得一提的是,,尽管 git 可以检测到文件内容不变而路径改变的情况, 但是它不会去显式的记录文件的更名操作,所以肯定不存在 svn 的树冲突。

分布式管理

1、分布式版本控制系统没有“中央服务器”,每个人的电脑上都有一个完整的版本库。因此工作的时候,可以不需要联网就可以提交本地仓库。

2、分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了可以直接从其他人那里复制一个。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法提交和继续工作。

增加暂存区

  • 实现部分提交
  • 无需在工作区中创建状态文件,不会污染工作区。
  • 暂存区记录文件的修改时间等信息,提高文件比较的效率

4、git操作流程

初始化

添加远程仓库

如果是一个空的工程,得先 git init,再用以下方式进行添加:

删除远程仓库

如果想更在同一个目录下,更换另一个仓库,则可以先删除再添加,也可以直接更换远程仓库,如下三种方式:

  • 删除:git remote rm origin
  • 更换:git remote set-url origin url
  • 配置中手动修改:.git/config 的 url

拉取

git pull

拉取远程库上的最新代码更新合并到本地当前分支。

git fetch

只是将远程的数据拉到本地仓库,并不自动合并到当前分支,需要手动合并。

提交

git add
  • git add -A:添加所有的修改到 staged area
  • git add .:添加 untracked file 和 tracked file 文件的修改到缓存区,注意会忽略掉文件的删除操作,比如你删除了文件Android.mk,执行这个命令并不会把把删除操作提交到 staged area。
  • git add -u:添加 tracked file 的修改到缓存区,注意会忽略掉 untracked file,也就是新增的文件。比如编译过程中产生了很多新的中间文件,使用此命令可以忽略掉这些中间文件。
git commit

git commit -m “BugID:XXX:Description” 常用的一种格式。注意 commit message 格式错误可能会导致入库失败。
还有一种方式是直接执行 git commit 命令,再打开的新窗口中输入 commit message。

git commit -m “message” 提交暂存区到本地仓库,message代表说明信息
git commit file1 -m “message” 提交暂存区的指定文件到本地仓库
git commit -a 提交本地暂存区和工作区的文件到本地仓库
git commit –amend -m “message” 使用一次新的commit,替代上一次提交
git push

git push remote_repository loacl_branch:remote_branch

例如:git push orgin master:refs/for/master 提交本地的master分支的修改到远程库 origin 的 refs/for/master 分支

常用的格式如下

git push remote branch 上传本地指定分支到远程仓库
git push remote –force 强行推送当前分支到远程仓库,即使有冲突
git push remote –all 推送所有分支到远程仓库

查看

git log

常用的几种组合是:

  • git log 查看提交记录。
  • git log -p 查看每次提交的详细修改。
  • git log –pretty=oneline 每个 commit 只显示一行,这个命令在处理代码冲突 conflict 时非常有用。
  • git log –name-status 查看每次提交修改的文件。
git blame

查看修改记录:

  • git blame “filename”:查看文件的每一行修改记录。
  • git blame -L 开始,结束行 “filename”:查看文件从开始行到结束行的修改记录。
git status

查看文件处于什么状态,会显示 untracked,和 tracked 中的文件。

git 命令时带上目录,比如git status ./build 只查看build目录下的代码状态

回退

git checkout

回退在工作区中修改的文件,比如修改了文件a.txt,还没有执行git add命令,这时候要丢弃这些修改,可使用命令:git checkout – a.txt。

git reset

回退暂存区或仓库中的提交记录。常用的几种组合有:

  • git reset –soft “commit”:回退到某个版本,只回退 local repository。
  • git reset –hard HEAD:回退暂存区的 add,让工作区回到上次提交时的状态

  • git reset –mixed “commit”:此为默认方式,同不带参数的 git reset,只保留 woking directory 的修改,回退 staging area 和 local repository的修改

  • git reset –hard “commit”:彻底回退到某个版本,本地的源码也会变为上一个版本的内容,比如 git reset –hard HEADgit reset –hard HEAD
git revert

撤销提交,是用一次新的 commit 来回滚之前的 commit,HEAD 继续前进。而 git reset 是直接删除指定的 commit,并把 HEAD 向后移动了一下。

暂存

git stash
  • git stash save ‘message’:保存当前工作进度,即保存在工作区,还未提交的
  • git stash list:显示保存进度的列表
  • git stash apply “stash_id”:恢复进度列表,不删除
  • git stash drop “stash_id”:删除某个进度
  • git stash pop “stash_id”:恢复进度列表,并删除
  • git stash clear:删除所有存储进度

分支操作

多人合作或有一些新需求时,往往需要开辟一些新功能的分支,此时若想把功能分支合并到主干上,如下所示,想把 feature 分支上的 C4 和 C6 提交合并到 master 分支上:

git_merge_1

常用的有俩种方法:git merge 和 git rebase。

git merge

使用 git merge 把 feature 分支合并到 master 分支后,记录如下所示:

git_merge_2

merge 时,会根据 mater 和 feature 的最新节点和共同的祖先节点进行合并,并生成一个新的提交。

  • merge 分支的提交历史也被原样保存
  • 会生成一个额外的合并提交
  • 历史提交不是线性的
git rebase

在 master 使用 git rebase feature 后,记录如下所示:

git_merge_3

在 master 上,把 rebase feature 分支的提交 C4,C6 移到共同祖先的起点 C2,但丢弃原本的,而是创建一个新的提交 C4’, C6’,重写了提交历史。

  • 把 rebase 分支上的提交历史重写了
  • 不会生成额外的提交
  • 历史的提交记录是线性的

当改写了公有的 commit history,当 git push 时,本地和远程的 commit history 不一致时,commit 会被拒绝,尽量不要使用 -f 强制 commit,这会造导致别人工作成果的丢失。比较好的习惯是约定:拉取 git fetch 后,再用 git rebase origin/maste 修改本地自己的 commit ,而不影响别人的提交历史,最后再 git push 提交。

rebase 常用命令:

  • git rebase –continue:当 rebase 出现冲突时,解决完冲突后 git add 添加暂存,就可以使用 git rebase 继续执行。
  • git rebase –abort:放弃此次 rebase。
  • git rebase -i HEAD~n:将几次提交合并为一次,再使用 squash 将几次提交合并到前一次。
git cherry-pick

选择某一个分支的一个或多个 commit 合并:

  • git cherry-pick “commit id”:单独合并一个提交。
  • git cherry-pick -x “commit id”:同上,但保留原提交者信息。
  • git cherry-pick “start-commit-id”..”end-commit-id”:左开右闭,不包含 start-commit-id的提交cherry-pick到当前分支。
  • git cherry-pick [start-commit-id]^..[end-commit-id]:闭区间,包含 start-commit-id 的提交 cherry-pick 到当前分支。
git branch
git branch 列出所有本地分支
git branch -r 列出所有远程分支
git branch -a 列出所有本地分支和远程分支
git branch “branch-name” 新建一个分支,但依然停留在当前分支
git checkout -b “branch-name” 新建一个分支,并切换到该分支
git branch –track “branch” remote-branch 新建一个分支,与指定的远程分支建立追踪关系
git checkout “branch-name” 切换到指定分支,并更新工作区
git branch -d “branch-name” 删除分支
git push origin –delete “branch-name” 删除远程分支
知道是不会有人点的,但万一被感动了呢?