Git入门手册

Git可以说是目前最优秀的分布式版本控制系统,并且使用Git还能提升逼格,何乐而不为啊。所以记录下来学习git的过程,以方便以后查阅。

# 本机环境

  • Win10
  • git version 1.9.5

# 基础知识普及
Git系统中一共有三个区域:

  • 工作区。用户能看到的文件夹下的文件改动。
  • 暂存区/阶段区(stage area)。指git认为的下一个提交(commit)时应包含的内容。一个文件通过add指令就会被添加到暂存区中。
  • 提交区。指工作目录下.git目录下的内容。

创建仓库(repository)

创建仓库主要分两种:

  1. 对本地文件夹进行git管理
  2. 克隆远程仓库

初始化本地文件夹为git仓库(在e:/gitest/first目录下创建)

$ git init
Initialized empty Git repository in e:/gitest/first/.git/

把文件修改添加到暂存区

$ git add hello.txt

查看当前工作状态

$ git status

克隆远程仓库(第一个参数为远程仓库地址,httpsssh地址都可以。第二个参数为本地目录,不填则默认为当前目录)

$ git clone https://github.com/lyszZi/remote.git /e/gitest/remote

commit用来提交修改到仓库中。每次commit后,暂存区的内容都会被清空。这个命令中的参数 -a 意味着提交工作区中所有修改过的文件以及删除的文件(但不包括新添加的文件),不使用 -a 则默认只提交暂存区中的内容。此外,还可以使用 -m 给commit添加注释,提高可读性。

$ git commit -a -m "add hello.txt"

分支(branch)管理

  多个分支的概念实际上是基于某次提交(commit)创建的两个不同的记录,每个分支上的提交都是基于该分支的最后一次提交。
  创建分支可以是基于某个已有的分支,也可以是基于某次提交(commit)。

创建一个新分支development,这种创建分支方法是基于当前分支的最新提交来创建的。

$ git branch development

基于某个特定的提交节点创建分支,首先我们需要知道该节点的标识(随后会介绍,这里只需要知道每个commit都有一个40位的唯一的标识)。

$ git branch another 86f756645

切换当前工作区为某个分支。第二个命令为创建一个新的分支并切换工作区为该分区,其效果可以等价于连续执行branchcheckout指令。

$ git checkout 分支名称

$ git checkout -b 分支名称
等价于:
$ git branch 分支名称
$ git checkout 分支名称

查看当前仓库中有哪些分支。

$ git branch
$ git branch --list 

删除分支。这里的删除分支只是删除分支名字而已(即使用git branch无法再查看到该分支名称)。但是这个分支中所有已提交的内容都不会被删除,接下来会介绍如何恢复这些“无引用”的分支。

$ git branch -d 分支名称

git merge 用法

merge的语义即为 merge from,意味着将指定的分支合并到当前分支。

先用checkout切换到要合并的分支。然后执行git merge指令把目标分支合并到当前分支。

$ git checkout master
$ git merge development

以上的指令把分支development合并到了分支master中,如果合并过程发生冲突(两个文本文件内容不一致),系统会以特殊的格式插入到文本中,一般的冲突格式如下:

<<<<<<< HEAD
third
=======
add dev third
>>>>>>> development

PS:解决冲突的方式只能是手工修改冲突内容,然后再重新提交文件.如果合并的文件没有发生冲突,则会自动将合并的内容提交

git merge指令还有以下常见的参数选项:

  • --commit :即merge后自动进行commit。系统默认为该选项。
  • --no-commit :即merge后只合并工作区中的内容,不进行commit操作。
  • -m :merge后自动提交时添加的注释。

git rebase 用法

rebase意为改变当前分支的基点。

$ git checkout development
$ git rebase master

以上的指令意为,更改当前分支development将以最新的master分支最为基点。也就是说最新的master中的内容会合并到development中。如果合并的过程中发生了冲突,将会和 merge 操作一样处理冲突问题。

git cherry-pick 用法

cherry-pick意为寻找最好的樱桃。即在众多的对象中挑选最优的一个。

把标识为02a7e2edc的提交合并到当前分支中。

$ git cherry-pick 02a7e2edc

PS: Git中存储的提交都是以快照(snapshot)的形式,而不是svn的增量(delta)形式,所以Git不需要进行额外的增量运算就可以直接合并或恢复文件,这极大加快了合并速度。

merge,rebase和cherry-pick的区别

  • cherry-pick: 侧重于吧某个模块功能分支中的某次提交应用到主干分支
  • git merge: 侧重于把两个分支合并。
  • git rebase: 侧重于更新模块功能对应的基点。

git reset 用法

reset将当前分支的头(HEAD)恢复到之前某个提交节点。换种说法就是忽略或者丢弃最近的某些提交操作。
reset有两种模式,分别为软复位和硬复位。

  • –soft: 仅复位提交区的内容,工作区和暂存区的内容保持不变。
  • –hard: 三个区域的内容都被复位。

软复位到HEAD的前两个提交节点。

$ git reset --soft HEAD~2

软复位到某个特定提交节点。需要获得该提交节点的标识。

$ git reset --soft 02a7e2edc

# 软复位后一般都伴随一次commit操作来完成操作。

硬复位。硬复位意味着丢弃之前的修改,软复位意味着合并之前的修改。

$ git reset --hard HEAD``

#复位操作不仅能往前复位提交节点,还能向后复位,只要知道提交节点的标识即可。

恢复“无引用”提交

恢复“无引用”提交的方法主要有三种,分别是针对于三种不同原因导致的无引用情况后的处理情况。但是无论是哪一种方式,首先都要先找出“无引用”提交节点的标识!!接下来只要在基于该提交节点新建一个分支就可以了。

  1. git log: 在reset前调用过git log查看记录,reset之后可以根据之前的log记录恢复到某个之前的节点。
  2. git reflog: 列出HEAD的变化历史。系统中只有一个HEAD。这个方式适合同一个分支中由于reset而引起的“无引用”情况。只要通过git reflog找到该“无引用”提交节点的标识,在创建分支即可。
  3. git fsck -full: 列出所有未被引用的提交。适合于删除某个branch并手动删除.git/logs/目录而引起的“无引用”情况。

PS:手动删除.git/logs/目录有风险,不推荐!要慎重!

git remote 用法

remote用于和远程仓库进行交互。本地远程仓库有两个分支

在本地建立一个远程仓库的引用。该命令只是在本地建立一个引用,本地还没有任何别的远程仓库的内容。

$ git remote add name git_url

PS:如果是没有关联远程仓库的本地仓库,则需要使用该命令来与远程仓库建立连接;如果本地仓库为clone远程仓库的,则会默认有一个对远程仓库master分支的引用origin。

查看本地仓库有哪些远程仓库引用。

$ git remote
$ git remote -v

查看远程仓库(origin)中的分支。

$ git remote show origin

将本地内容push到远程仓库中。src代表本地分支名称,des是远程分支名称。不写des则默认src与des相同。

$ git push origin src:des

删除一个远程分支,把一个空的内容push到远程分支,就意味着删除该分支。

$ git push origin :des

克隆远程仓库。更多可查询fetch,pull。

$ git clone git@github.com:lysongzi/remote.git

创建一个远程仓库分支的本地跟踪分支。

$ git checkout -b dev origin/development

//自动创建一个名为name的本地分支,作为origin/name的跟踪分支。
$ git checkout --track origin/name

接下来介绍两个应用场景。一个为将本地仓库同步到一个新建的远程仓库,另一个为从远程仓库同步内容并创建本地仓库。可以参考此远程多人协作模式(传送门-多人协作模式)。

场景1.(需要ssh连接—传送门-配置ssh密钥)

//创建一个远程仓库的引用
$ git remote add origin git@github.com:lysongzi/remote.git

//将本地的某个分支push到远程仓库中
$ git push origin master
$ git push origin development

//从现在开始,只要本地做了提交commit,就可以通过push命令把分支的最新修改推送到远程仓库中。

情景2.

//使用clone克隆远程仓库到本地。
$ git clone https://github.com/lysongzi/remote.git

//此时本地将只有远程仓库的master分支,我们再创建对development分支的引用。  
//origin为系统创建的对远程仓库的master分支的本地跟踪分支。
$ git checkout -b dev origin/development

//然后我们就可以在本地上对各个分支进行修改,提交,最后选择性把分支的修改push到远程仓库对应分支即可。

然后再实际多人协作过程中,别人也会向远程仓库的某个分支中提交他们的修改。有时候,我们的修改和别人的修改就会产生冲突。

$ git push origin dev
To git@github.com:lysongzi/remote.git
 ! [rejected]        dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@github.com:lysongzi/remote.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

就是说别人更新了修改,和我们的修改产生了冲突。根据Git提示,我们需要先pull一些最新的修改,如果有冲突,则按冲突解决方法解决冲突,然后重新提交。

$ git pull
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From github.com:lysongzi/remote
   fc38031..291bea8  development    -> origin/development
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details

    git pull <remote> <branch>

If you wish to set tracking information for this branch you can do so with:

git branch --set-upstream-to=<remote>/<branch> development 

然而这时候还是出现了报错,根据提示原因是没有指定本地dev分支与远程origin/dev分支的链接。

$ git branch --set-upstream-to=origin/development development

再次执行pull操作。

$git pull

git pull指令会同步本地仓库和远程仓库的内容,并把有冲突的地方标识出来。然后解决了冲突后,提交,再push则没有问题了。

#多人协作模式总结

  1. 首先,可以试图用 git push origin branch 推送自己的修改;

  2. 如果推送失败,则说明远程分支比你的本地更新,需要先用 git pull 试图合并;

  3. 如果合并有冲突,则解决冲突,并在本地提交;

  4. 没有冲突或者解决掉冲突后,再用 git push origin branch 推送就能成功!

  5. 如果 git pull 提示 “no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令 git branch --set-upstream-to=<remote>/<branch> 本地branch 来创建。

git 配置

git的配置主要有两种方式,一种是通过命令行来配置,还有一种则是修改配置文件。更多参考 传送门—Git配置

  • 基本信息类配置。
  • 第三方工具类配置。
  • gitignore配置。即告诉Git系统排除对那些类型文件的管理。

且系统中主要有三种配置文件。这三种文件优先级为 3>2>1

  1. /etc/gitconfig : 全局配置。
  2. ~/.gitconfig : 适用于当前用户。
  3. .git/config : 适用于当前工作仓库。

命令行分别为:

$ git config     --system name value
$ git config     --global name value
$ git config     --local name value

配置基本信息(提交者名称,邮箱)。

$ git config user.name 'xxxx'
$ git config user.email 'xxx@xxx.xxx'

查看当前配置,查看某个属性的值。

$ git config -l或--list
$ git config prop_name

其他常用命令合集

git help

查看各个命令的完整用法。

$ git help command-name

git log

限制记录输出。

$ git log -l -5

每条Log只显示一行。

$ git log --pretty=oneline

查看两个提及节点间的Log,since和untill为提交节点对应的标识。

$ git log since...untill

git diff

查看两个分支的不同。

$ git diff 分支1 分支2

查看后者(dev)与前者(master)共同父节点之间的差异。

$ git diff master...dev

比较当前工作区内容与上一个提交之间的差异。

$ git diff HEAD

比较当前分支的暂存区和上一个提交之间的差别。如果当前修改没有被添加到暂存区,输出为空。

$ git diff --cached HEAD

git rm

该指令作用与git add相反。

移除文件或目录。-r递归删除,-f强制删除。

$ git rm -rf 文件/目录

git tag

Git的标签虽然是版本库的快照,但其实它就是指向某个commit的指针(跟分支很像对不对?但是分支可以移动,标签不能移动),所以,创建和删除标签都是瞬间完成的

查看所有tag。还可以使用通配符查询标签。

$ git tag
$ git tag -l 'v1.*'

首先,切换到需要打标签的分支上。然后创建标签即可。

$ git tag v1.0

对某个提交打标签。

$ git tag v1.1 6224937

标签不是按时间顺序列出,而是按字母排序的。可以用git show 查看标签信息。

$ git show v1.0

指定标签信息。

$ git tag -a v1.0 -m "注释"

删除标签。

$ git tag -d v1.0

参考资料

1.《Android内核剖析》第四章
2.廖雪峰个人网站-git教程