[关闭]
@KalingYu 2017-07-14T08:40:02.000000Z 字数 12685 阅读 781

Git 基本用法和基本概念详解

git git基础



基本概念

工作方式

基础

Git 和其他版本控制系统的主要差别在于,Git 只关心文件数据的整体是否发生变化,而大多数其他系统则只关心文件内容的具体差异。这类系统(CVS,Subversion,Perforce,Bazaar 等等)每次记录有哪些文件作了更新,以及都更新了哪些行的什么内容

在保存到 Git 之前,所有数据都要进行内容的校验和(checksum)计算,并将此结果作为数据的 唯一标识和索引。换句话说,不可能在你修改了文件或目录之后,Git 一无所知。这项特性作为git的设计哲学,建在整体架构的最底层。所以如果文件在传输时变得不完整,或者磁盘损坏导致文件数据缺失,Git 都能立即察觉。
计算校验和的算法是 SHA-1.

Git 挂钩

概念

当发生一些事情(如提交合并)的时候,Git 调用自己的脚本。有客户端和服务端两种。

安装

将正确命名且可执行的脚本放在 git 目录下的 hooks 子目录中。

客户端挂钩

文件的三种状态

1. modified (在工作目录,in working directory)
2. staged (在暂存区域,in staging area)
3. committed (在本地仓库,in git directory(repository))

文件的三个工作区域

1. 工作目录: 从项目中取出某个版本的所有文件和目录,用以开始后续工作的叫工作目录。
2. 暂存区域: 放在 git 目录中的一个简单文件,人们常称之为索引文件。
3. 本地仓库: 

三个工作区域关系如图

git文件的三种状态示意图

工作流程

1. 在工作目录中修改某些文件。
2. 对修改后的文件进行快照,然后保存到暂存区域。
3. 提交更新,将保存在暂存区域的文件快照永久转储到 Git 目录中。

名词解释

合并(merge)

概念解释

把两个分支最新的快照(C3 和 C4)以及二者最新的共同祖先(C2)进行三方合并,合并的结果是产生一个新的提交对象(C5)

用途

最常用的

衍合(rebase)

概念

把在 C3 里产生的变化补丁在 C4 的基础上重新打一遍。

用途

挑拣(cherry-pick)

概念

顾名思义,就是挑选另外一个分支历史上某次特定的提交来进行衍合

用途

维护者通常喜欢这么干,以保持一个线性的提交历史

分支(branch)

概念

分支其实就是从某个提交对象往回看的历史

用途

这个就不用说了

上游(upstream)

想像分叉的更新(分支)像是汇流成河的源头(其实提交的历史就是一个链表,当前的提交总是指向上一次的提交:LastTime Commit <- Current Commit),所以上游 upstream 是指最新的提交

upstream 和 downstream 另外一个维度的概念

git中存在upstream和downstream,简言之,当我们把仓库A中某分支x的代码push到仓库B分支y,此时仓库B的这个分支y就叫做A中x分支的upstream,而x则被称作y的downstream,这是一个相对关系,每一个本地分支都相对地可以有一个远程的upstream分支(注意这个upstream分支可以不同名,但通常我们都会使用同名分支作为upstream)。


安装

  1. sudo apt-get install git git-core

这是来自其它帖子的代码,不过我觉得 git-core 可以不要,因为根据linux社区的说法(如下),这玩意儿已经被废弃了,直接 intall git 就OK了

this(git-core) is a transitional dummy package. the 'git-core' package has been renamed to 'git', which has been installed automatically. this git-core package is now obsolete, and can safely be removed from the system if no other package depends on it.

  1. brew install git

注:brew 是 mac 的包管理 homebrew 的命令。没有 homebrew 的,点这个链接安装homebrew


使用

快速入门

在命令行中创建仓库

  1. mkdir demoDir #创建名为demoDir的工作目录
  2. cd demoDir #进入工作目录
  3. git init #初始化 git
  4. echo "# demoDir" >> README.md #创建 README.md 文件
  5. git add README.md #添加文件追踪,偷懒的直接 git add .
  6. git commit -m "first commit" #提交更新,引号里面是更新日志
  7. git remote add origin https://git.coding.net/Kaling/markdown-photos-repository.git #添加远程仓库,origin 是远程仓库名,这个可以自定义。
  8. git push -u origin master #将更新推送到远程仓库

已有项目

  1. git remote add origin https://git.coding.net/Kaling/markdown-photos-repository.git
  2. #在这里创建或者修改文件,然后用下面的 git commit -am 提交
  3. git commit -am "this is a demo"
  4. git push -u origin master

其实还是git clone [remote-url]来得简单粗暴

运行配置

基本配置

Git有一个工具被称为git config,它允许你获得和设置配置变量;这些变量可以控制Git的外观和操作的各个方面。这些变量可以被存储在三个不同的位置:/etc/gitconfig、~/.gitconfig、.git/config

  1. git config --list

配置用户信息

  1. git config --global user.name "余嘉陵"
  2. git config --global user.email "yujialing94@gmail.com"

SSH key 的生成和添加(我怎么记得这个在哪个地方已经写过了)

  1. 查看是否已经有了 ssh 秘钥:cd ~/.ssh,如果没有秘钥则不会生成这个文件夹,有则备份、删除
  2. 生成秘钥:
  1. ssh-keygen -t rsa -C "yujialing94@gmail.com"
  1. 添加秘钥到ssh:
  1. ssh-add <文件名>
  1. 如果一切顺利的话,在~/.ssh 目录下有了 id_rsa(私钥) 和 id_rsa.pub(公钥) 文件。复制公钥里面的内容。
  2. 打开github/gitlab,将公钥粘贴到里面。

换行符处理

  1. git config --global core.autocrlf input
  1. git config --global core.autocrlf true

注:配置换行符是为了避免不同操作系统由换行符不同而导致协作出现状况,详情戳这里

配置文本编辑器

  1. git config --global core.editor vim

配置差异分析工具

  1. git config --global merge.tool vimdiff

不过我觉得这个没有多大的必要,因为其实 android studio 等Jetbrains那一套IDE有很好都冲突解决工具。

查看配置信息

  1. git config --list
  2. git config user.name

获取帮助

  1. git help <verb>
  2. git <verb> --help
  3. man git-<verb>

常用命令

创建仓库

创建仓库并进行远程工作通常有两种方式:

1)在当前工作目录中初始化仓库

  1. git init

2) 添加远程仓库

  1. git remote add origin https://git.coding.net/Kaling/markdown-photos-repository.git

3) 拉取远程更新

  1. git pull
  1. git clone https://git.coding.net/Kaling/markdown-photos-repository.git
  1. git remote show mp
  1. git tag

新建标签

记录每次更新到仓库

现在已经有了一个真实的 git 仓库。工作目录下的文件只存在两种状态: 已跟踪和未跟踪。已跟踪的文件是指本来就被纳入版本控制管理的文件,在上次快照中有它们的记录,工作一段时间后,它们的状态可能是未更新,已修改或者已放入暂存区。而所有其他文件都属于未跟踪文件。它们既没有上次更新时的快照,也不在当前的暂存区域。初次克隆某个仓库时,工作目录中的所有文件都属于已跟踪文件,且状态为未修改。

在编辑过某些文件之后,Git 将这些文件标为已修改。我们逐步把这些修改过的文件放到暂存区域,直到最后一次性提交所有这些暂存起来的文件,如此重复。所以使用 Git 时的文件状态变化周期所示。
文件状态变化周期

检查当前的状态
  1. git status
创建README 文件

用 vim 或者你习惯的编辑器创建README 文件,对仓库代码进行说明

  1. vim README

这时用 git status 查看会发现 Untracked files 中出现了 README 这个文件,并提示你用git add <file> 命令添加文件跟踪

跟踪新文件
  1. git add README

git status 查看状态,只要是在 changes to be commited 下面的文件都是已经处于暂存状态的了,而这个时候 README 文件在这个下面。

暂存已修改的文件

也是git add <file>命令。这是个多功能命令,根据目标文件的状态不同,此命令的效果也不同:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等。
其中git add * 指暂存、添加所有(.gitignore中限制的除外)文件到已跟踪状态。

忽略某些文件

创建 .gitignore 文件

  1. vim .gitignore

文件 .gitignore 的格式规范如下:

ps:glob 模式说明。所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。星号(*)匹配零个或多个任意字符;[abc] 匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)。

代码示例:

  1. # 此为注释 – 将被 Git 忽略
  2. # 忽略所有 .a 结尾的文件
  3. *.a
  4. # 但 lib.a 除外
  5. !lib.a
  6. # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
  7. /TODO
  8. # 忽略 build/ 目录下的所有文件
  9. build/
  10. # 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt
  11. doc/*.txt
查看文件修改后的差异
  1. git diff
  1. git diff --staged
  1. git diff --cached
提交更新
  1. git add . #将已跟踪的文件全部添加到暂存区
  2. git commit -m #添加更新的说明,可以不添加参数m 而使用文件编辑器添加更多的说明

或者直接使用git commit -am, -a 会直接将已经追踪的文件提交。

移除文件
  1. git rm <file>
  1. git rm --cached <file>
移动文件
  1. git mv README.txt README

这句话相当于

  1. mv README.txt README
  2. git rm README.txt
  3. git add README
查看提交历史
  1. git log

常用 -p 选项展开显示每次提交的内容差异,用 -2 则仅显示最近的两次更新。

撤销操作
  1. git commit --amend

如果刚才提交时忘了暂存某些修改,可以先补上暂存操作,然后再运行 --amend 提交, 以下三条命令最终只是产生一个提交,第二个提交命令修正了第一个的提交内容。

  1. git commit -m 'initial commit'
  2. git add forgotten_file
  3. git commit --amend

远程仓库的使用

查看远程仓库
  1. git remote

git remote 命令常用参数说明:

添加远程仓库

使用git remote add [remote-name] [url] 命令

  1. git remote add mp https://git.coding.net/Kaling/markdown-photos-repository.git

这个时候, mp 就指代了这个远程仓库。

远程仓库常用操作
  1. git fetch [remote-name]
  1. git pull
  2. git pull <remote-branch> : <local-branch> #只 pull 一个远程分支

如果当前分支与多个主机存在追踪关系,则可以使用-u选项指定一个默认主机,这样后面就可以不加任何参数使用git push。

  1. git push -u [remote-name] [local-branch-name]

如果当前分支只有一个追踪分支,那么主机名都可以省略。

  1. git push

将本地的 branch-name 分支推送到 remote-name 远程服务器上。

  1. git push [remote-name] [local-branch-name]:[remote-branch-name]

git push 默认是提交当前本地分支追踪的同名的

如果省略远程分支名,则表示将本地分支推送与之存在”追踪关系”的远程分支(通常两者同名),如果该远程分支不存在,则会被新建。

  1. git push [remote-name] [local-branch-name]

如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略,直接将当前分支推送到origin主机的对应分支。

  1. git push [remote-name]

不管是否存在对应的远程分支,将本地的所有分支都推送到远程主机,这时需要使用–all选项。

  1. git push --all [remote-name]

如果远程主机的版本比本地版本更新,推送时Git会报错,要求先在本地做git pull合并差异,然后再推送到远程主机。这时,如果你一定要推送,可以使用–force选项。

  1. git push --force origin

结合git pull的参数,会发现后面的分支顺序是<来源地>:<目的地>.

删除远程分支。

  1. git push [remote-name] :master
  2. #等同于
  3. git push [remote-name] --delte master

git push不会推送标签(tag),除非使用–tags选项。

  1. git push [remote-name] --tags
TAG 的使用

标签分为两种:轻量级的(lightweight)含附注(annotated)的 。前者是指向一个特定提交的引用,后者是存贮在仓库中的一个独立对象,有自身的校验信息和标签说明等。一般建议使用含附注的标签
- 新建含附注的标签。-aannotated(附注) 的首字母。m则是message

  1. git tag -a v1.4 -m "my version 1.4
  1. git tag v1.4-lightweight
  1. git show v1.4

分支的使用

你创建了很多个分支,Git 如何知道你现在在哪个分支上工作呢?
事实上它保存着一个名为HEAD 的特别指针,指向当前分支。可以用git checkout branch-name切换 HEAD 指向的分支,以达到切换分支的目的。

查看分支
  1. git branch
  1. git branch -vv #查看分支的追踪
  1. git branch -a
创建分支
  1. git branch new-branch-name
切换本地分支
  1. git checkout new-branch-name
远程分支
  1. git checkout --track origin/dev #切换到远程的 dev 分支
删除分支
  1. git branch -d branch-name
合并分支

合并分支分为 git merge 和 git rebase,两者都是将一个分支的代码合入另外一个分支,但是原理b不一样。

优点:merge是一个安全的操作,现有的分支不会被更改,避免了rebase潜在的缺点。
缺点:会带来额外的合并提交,如果merge比较频繁的话,或多或少会污染分支历史,增加项目历史的难度。

  1. git checkout mywork
  2. git merge origin

rebase为原分支的每一个提交都创建了一个新的提交,重写了项目历史,不会带来合并提交。

优点:最大的好处是你的项目历史会非常整洁,是一条漂亮的线性历史,不会像git merge那样引入不必要的合并提交。

缺点:使用rebase会带来两种后果:安全性和可追踪性。如果违反了rebase黄金法则,重写项目历史可能会给协作带来灾难性的影响。此外rebase不会有合并提交中附带的信息。

  1. git chekcout mywork
  2. git rebase origin
重命名本地分支
  1. git branch -m [old-branh] [new-branch]
删除远程分支
  1. git push --delete orgin [remote-branch-name]
提交本地分支到远程分支
  1. git push origin test:master # 提交本地的 test 的分支到 远程的 master 分支
将本地分支与远程分支进行关联(设置当前分支为远程某分支的上游(upstream))
  1. git branch --set-upstream local-branch-name origin/remote-branch-name
获取远程分支remoteName 到本地新分支localName,并跳到localName分支
  1. git checkout orgin/remote-branch-name -b local-branch-name
重命名远程分支

所谓的重命名远程分支也就是先删除远程分支,然后重命名本地分支,最后将本地分支推送到远程分支

  1. git push --delete origin [remote-branch-name] #删除远程分支
  2. git branch -m [old-branch-name] [new-branch-name] #重命名本地分支
  3. git push origin [new-branch-name] #推送本地分支到远程
给分支打补丁

打补丁有两种方案 git diffgit forma-patch, 前者生成通用的补丁,可用于各种版本控制工具;后者生成git 专用补丁,可用git am 「补丁文件名」这条命令打上。推荐使用后者。使用过程如下

  1. # 在需要生成补丁的分支中打补丁,这会将所有的提交都打成一个补丁。M参数表示和后面的值表示和 master 分支进行对比
  2. git format-patch -M master
  3. # 这会生成补丁文件,比如补丁文件名为 0001-.patch. 切换到 master 分支,新建并切换到 patch 分支,然后打补丁
  4. git am 0001-.patch

图形界面工具推荐

提交指南


团队协作

私有小团队(私有项目)

私有团队间协作

艾玛,这玩意儿编辑起来太累了,如果要理解git的工作方式就直接看上面的那个,关于私有团队的协作还是直接点这个链接吧。

公开的小型项目

同上理,点这个链接

公开的大型项目

同上上理,点这个链接

项目的管理

要点
使用分支特性进行工作
采纳补丁
检出远程分支
  1. 先把对方的仓库加为远程仓库
  2. 抓取数据
  3. 将对应的特性分支检出到本地进行测试
    示例代码
  1. $ git remote add jessica git://github.com/jessica/myproject.git
  2. $ git fetch jessica
  3. $ git checkout -b rubyclient jessica/ruby-client
代码集成
给发行版签名
准备发布

一些奇技淫巧

查看 log 时屏蔽掉不需要的分支,以防止干扰和不必要的麻烦

例如在issue01特性分支上屏蔽master分支的提交历史

  1. git log issue01 --not master

创建并切换到新分支

创建基于jessica的ruby-client的新分支ruby-client

  1. git checkout -b rubyclient jessica/ruby-client

使用简短的SHA

用SHA-1中开头4个字符(及以上)就能标识/引用/索引那一次 commit

储藏工作目录中的中间状态,然后进入另外一个分支上进行工作

经常有这样的事情发生,当你正在进行项目中某一部分的工作,里面的东西处于一个比较杂乱的状态,而你想转到其他分支上进行一些工作。问题是,你不想提交进行了一半的工作,否则以后你无法回到这个工作点。

解决这个问题就是用 git stash 命令,获取工作目录中的中间状态,将变更保存起来,并随时切回来重用。
步骤:
1. 用 git status 查看中间状态
2. 使用 git stash 命令暂存当前变更
3. 可以用 git status 验证当前并没有待提交的变更
4. 查看当前的储藏 git stash list
5. 切回分支应用储藏 git stash apply(可添加一个 stash 名字作为参数,如无则默认使用最近的储藏)

用 Git 属性比较二进制文件

// todo

Git 命名别名

// todo

git stage 替代 git add,这样的话,更方便,可以「分批次提交」,降低commit 的颗粒度

相关文档:http://dudu.zhihu.com/story/8981865
1. 分批递交,降低commit的颗粒度
比如,你修改了 a.py, b.py, c.py, d.py,其中 a.py 和 c.py 是一个功能相关修改,b.py,d.py属于另外一个功能相关修改。那么你就可以采用:
git stage a.py c.py
git commit -m "function 1"
git stage b.py d.py
git commit -m "function 2"

  1. 分阶段提交
    比如,你修改了文件 hello.py,修改了一些以后,做了 git stage heello.py动作,相当于对当前的hello.py 做了一个快照, 然后又做了一些修改,这时候,如果直接采用 git commit 递交,则只会对第一次的快照进行递交,当前内容还保存在 working 工作区。

  2. 最佳实践
    做了阶段性修改,但是还不能做一次递交,这时先 git stage 一下
    如果有问题,可以随时 checkout 回退
    递交之前,使用 git status,git diff HEAD 仔细查看是否需要的递交
    git commit -a ,保证递交了所有内容


Git 内部原理

基本理解

从根本上来讲 Git 是一套内容寻址 (content-addressable) 文件系统,在此之上提供了一个 VCS 用户界面。


持续更新中……

参考

Pro Git 中文版
git-core. Fast, scalable, distributed revision control system (obsolete)
Git 分支 - 分支的衍合
git push 与 pull 的默认行为

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注