zhaoyu@home:~$

git 基础教程

原理

工作区和暂缓区

工作区就是指你电脑里面看到的目录,即电脑本地的修改,git还没有纳入到版本管理。 版本库即纳入git版本管理的内容,.git文件夹存放着版本库信息。版本库中存放了很多东西,其中最重要的是称为stage的暂存区。还有git默认为我们自动创建了一个master的分支,以及指向master的一个指针HEAD。

index &working tree&commit

working tree 表示工作区的改变,每当在代码中做了修改,working tree的状态就会改变。index file,是连接working tree和commit的桥梁。每当使用git add后,index file的状态就会改变。commit则表示代码库的状态。

操作

windows下安装git

msysgit是Windows版的Git,从http://msysgit.github.io/ 下载,然后按默认选项安装即可。 安装完成后,还需要最后一步设置,在命令行输入:

$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"

$ git config --global core.autocrlf false  #禁用crlf自动转换
$ git config --global core.safecrlf true  #拒绝提交包含换行符的文件。

$ git config --global gui.encoding utf-8  #gui编码设置。
$ git config --global i18n.commitencoding utf-8  #提交编码设置。
$ git config --global i18n.logoutputencoding gbk  #日志输出设置。

如果在windows下使用gitbash,右键点击,选择option,选择Text,地区选为zh_CN,字符集选为GBK。

git config

git的配置包括system global 和local三个级别,优先级从低到高。常用的为用户级别的global。local为当前仓库的配置信息。查看 配置使用如下命令:

git config --[system|global|local] --list

global的配置会生产在user目录下的.gitconfig文件中。

git add

用户将一个修改从工作区放到暂存区,这个过程也称为stage。

git add test.go

也可以使用正则表达式,如

git add *.txt

常用的是添加当前目录:

git add .

反之如果将暂存区的修改回退到工作区,称之为unstage。如

git reset test.go(状态变回modify)

unstage后,如果取消对test.go文件的修改,恢复为版本库中的状态,则使用

git checkout test.go

这个命令也可以用来恢复删除的文件。

git commit

  1. –amend:对上次的提交做修改,commitId也会被修改,不会产生新的提交。在修改很少的信息时,希望减少不必要的提交,amend很有用。 因为会对上次的提交改变commitId,所以在上次提交没有被推送时,才执行amend提交,否则会和远程库产生冲突。

git rm

删除文件。

git rm '*.txt'

git init

用于初始化一个git管理的文件夹,直接在命令行中,导向到目标文件夹中,执行git init

git diff

比如说,你早上上班记不清昨天修改了read.txt什么内容。可以使用命令查看:

git diff read.txt

git diff得到如下信息:

1  diff --git a/indexworkingtree.txt b/indexworkingtree.txt
2  index cb6c09d..e734c01 100644
3  --- a/indexworkingtree.txt
4  +++ b/indexworkingtree.txt
5  @@ -1,2 +1,5 @@
6   this is a test file
7   aim to understand working tree,index,commit
8  +
9  +add new line
10 +add new line second

第3-4行,—表示变动前的文件为a,+++表示变动后的文件为b。第5行中,-1,2表示a文件从第一行开始变动,一共改变了两行,+1,5表示b文件从第一行开始变动,一共修改了5行。6-10行表示两个文件改动部分的合并,前面为+表示增加,-表示删除,没有表示没有变动。

git diff –staged/git diff 显示working tree 和 index之间的不同, git diff –cached 表示index和commit之间的不同。 git diff HEAD 查看working tree 和commit之间的差别。

git log

git log命令可以查看到我们提交的历史记录

Administrator@SC-201506261217 MINGW64 /d/githubrepo/learndoc (master)
$ git log
commit c9893e7fd50f7a35df0f4be0a4a8c91c1a740117
Author: zhaoyu <zy4668@126.com>
Date:   Thu Nov 26 10:55:03 2015 +0800

    change markdown

commit da9a49e1117c31a2dd11d3585462d546892d2ab0
Merge: ce61285 2bb8e94

上面的显示可以看出我们最后一次提交为change markdown。 –pretty=oneline参数用于输出主要信息,省略其它信息:版本号和提交信息。

git reflog

如果你从最新的版本回退到旧的版本,然后再次退回到之前最新的版本,那么,就需要git reflog命令,git reflog命令记录了我们执行过的每一个命令。

$ git reflog
aacb99f HEAD@{0}: commit: chageit
cd4489f HEAD@{1}: commit (initial): init

根据上面显示的命令对应的版本号,我们可以回退到一个特定的版本。

git stash

git stash命令会将当前的工作空间“储存”起来,用HEAD重置working tree 和index。等以后恢复后继续工作,处理bug时非常有用,使用时:

git stash

在工作现场“储存”后,可以进行bug修复或者建立新分支等工作。恢复时使用

git stash apply

apply命令是保留了stash内容的,如果想恢复工作现场的同时删除stash内容,则使用pop命令:

git stash pop

使用list命令查看stash列表:

git stash list

如果有多个stash,则可以指定某个特定的恢复。

git stash apply stash@{0}

git ignore

git在工作区的目录下创建了一个.gitignore的文件,在这个文件中,把要忽略的文件名填进去,git自然会忽略这些文件。git文件需要提交到git库里。

有时候会碰到.gitignore无效,不能忽略某些文件, 现象:在.gitignore文件中添加了file1,以过滤该文件,但是通过git status查看,仍然能看到file1的状态。 原因:可能在file1在添加忽略文件之前,已经提交到git版本库。 解决方法:需要在git库中删除该文件,并更新。

git branch

git branch用于查看分支。

git branch #查看本地分支
git branch -a #查看所有分支,包括本地和远程的

如果后面跟分支名,就会创建一个新本地分支,如果分支已经存在,则返回错误。创建后依然停留在当前分支。

git branch dev

给分支重命名

git branch -m oldName newName

删除本地分支

git branch -d dev

强制删除本地分支

git branch -D dev

删除远程分支

git push origin --delete dev

推送本地分支到远程,有两个步骤:

  1. 创建远程分支。

     git push origin branch1:branch1 
    
  2. 关联远程分支。

     git push -u  origin remote_branch #-u 和--set-upstream相同
    

git checkout

git checkout可以操作文件和分支。

操作文件

git checkout filename # 从版本中检出某个文件,放弃对这个文件的修改。
git checkout .  #放弃对当下目录的修改。

操作分支

git checkout master # 将当前分支切换到master。
git checkout -b dev # 如果dev分支存在,则只切换分支,如果不存在,创建dev本地分支,并切换到dev分支上。
git checkout -b dev origin/dev #将远程库上的dev分支拉去到本地,并建立关联。

detached-HEAD

detached-HEAD通常是由于执行了如下命令

git checkout origin/branch

导致HEAD指向了一个非本地分支,这种状态非常危险,解决方法是,将目前分支保存为一个临时分支,然后再合并到另一个分支。

git reflog
git branch temp 1ebf64
git checkout --track origin/anotherBranch
git merge temp

这样就可以放心操作了

git merge

如将dev分支合并到主分支 1, 切换到主分支

git checkout master

2, 合并分支

git merge dev

3, 如果有冲突,则手到解决冲突,所有冲突解决完,使用git status确认。

git status

4, 停止合并,在合并有错误或者自己想停止合并,则可以将合并取消。

git merge --abort  #如果Git版本 >= 1.7.4
git reset --merge  #如果Git版本 >= 1.6.1

5, 当git仓库的状态是Merging时,需要处理项目中冲突的文本,解决冲突并确认合并无误后,提交修改到暂缓区。

git add .

6, 提交。

git commit –m “info”

Fast Forward 快进模式,

默认的git merge 是快进模式,合并后只能看到合并分支的信息,而不能看到被合并分支的信息。

* 88d416f (HEAD -> master) merge dev
* 65d0477 add master
* 4855945 init

No Fast Forward(–no-ff)。

在merge的时候,带上–no-off参数,在合并的分支上,会看到被合并分支的信息。如下

*   cde5be3 (HEAD -> master) merge dev
|\
| * e0c1d37 (dev) add dev info
* | 65d0477 add master
|/
* 4855945 init

–squash

默认的合并,会保留被合并分支中所有的提交,如果不想看到太多的无用commit信息,可以使用squash。 squash 参数,会将被合并分支的多个提交去除,在合并分支上只生产一次提交。

远程操作

远程操作需要本地关联远程库,默认在git clone时,会创建remotes/origin/master 分支,origin是运行clone时默认创建的远程仓库代码,可以使用-o修改。

git clone

  • 登陆GitHub,创建一个新的仓库,如learndoc
  • 查看GitHub的git库的地址,在客户端gitbash中输入如:

    git clone git@github.com:michaelliao/learndoc.git

    git clone会在本地创建一个master分支和remotes/origin/master 分支。

  • 如果是使用ssh下载,有可能弹出如下错误

    The authenticity of host 'github.com (192.30.252.129)'can't be established.
    

    在接下来的命令中输入yes继续尝试。

git remote

git remote 用于操作远程库,默认本地关联的远程库名称为origin。

git remote -v

用于查看远程库的地址信息,有fetch和push。如下:

origin  git@github.com:lovexiaoe/learndoc.git (fetch)
origin  git@github.com:lovexiaoe/learndoc.git (push)

更新远程分支,让本地和远程同步。

git remote update origin --prune

将已有文件夹,提交到远程库:

  • 在文件夹下初始化git:
 git init
  • 关联远程库
 git remote add origin git@120.25.147.61:zhaoyu/helpdrive.git
  • 提交本地代码
    git add .     &    git commit -m "init"
    
  • 推送到远程库
      git push -u origin master
    

    或者

      git push --set-upstream origin master
    

    第一条命令要保证远程库分支存在,如果不存在,无法进行关联,第二条即使远程库没有你关联的分支,它会自动创建并关联。

git fetch

将远程主机的最新内容拉到本地,用户在检查了以后决定是否合并到工作区中。如下,拉取master到本地库。

git fetch origin master

git pull

将远程库的最新内容直接拉到工作区中,git pull = git fetch + git merge。

git pull origin master  --allow-unrelated-histories

git 库和操作之间的关系如下:

git tag

执行打标签的一些列操作。标签用于记录版本,主要用于发布等场景,通常在发布的时候打上标签。

查看所有的标签。显示的结果按字母排列。

git tag

如果对特定名称的标签感兴趣,可以使用-l结合通配符查询。

git tag -l 'v1.2.*'

使用-a添加一个标签,使用-m 表示标签注释。

git tag -a v1.2.3 -m "1.2.3发布版"

获取项目最后的tag

git describe --tags `git rev-list --tags --max-count=1`

git push 并不会将tags推送到远程库,需要使用下面的命令。

git push origin v1.0
git push --tagas #推送所有tags。

git revert

git revert 用于去除了参数指明的历史提交,并生产一次新的提交,以前的历史记录不变。具体操作如下。

  1. 假设我们现在有如下提交:
    d3e93a7 (HEAD -> master) HEAD@{0}: commit: version4
    306dbc7 HEAD@{1}: commit: version3
    f6922a1 HEAD@{2}: commit: verions2
    4c4b831 HEAD@{3}: commit: version1
    
  2. 提交version2和version3由于代码有问题,我们需要去除。那么我们可以使用如下命令进行处理。
    git revert --no-commit 4c4b831..306dbc7
    

    revert后面的表达式是一个前开后闭的区间。即包括4c4b831,而包括了306dbc7。所以去除的是verion2和version3。
    注意:revert 命令会对每个去除的 commit 进行一次提交,–no-commit 后可以只生产一次提交。 如果revert的时候,代码有冲突,那么revert会报错,并且提交进入“REVERTING”状态

  3. 我们需要解决代码冲突,提交代码,执行
    git revert --continue 
    
  4. 重复以上两个步骤,直到REVERTING状态消失。

  5. 去除一组历史提交在代码冲突的情况下,操作过于复杂。所以建议每次去除单个提交,命令如下:
    git revert 306dbc7
    
  6. 最常用的就是去除HEAD提交,不会存在代码冲突。可以回滚最后一次提交,并生产一次新的提交。
    git revert HEAD
    

git reset

将HEAD指针指到指定提交,历史记录中不会出现放弃的提交记录。 git reset 一共有两种用法,

  1. 是在reset后加版本号,可以回退版本,如:
     git reset c9893e 或者 get reset HEAD^ (HEAD^表示HEAD的上一个版本) 
    

    git reset 常用的参数有–soft,–mixed,–hard。

    • –soft只回退commit信息。不会回退index,是git commit的反操作。如果要保留修改,推荐使用。
    • –mixed(默认),回退commit信息,并回退index。并将修改回退到工作区,是git add和git commit都执行的反操作。比较麻烦,stash等操作会冲突。不推荐使用。
    • –hard重置,回退commit信息,index,和工作区,所有的文件修改会丢失,谨慎使用。
  2. 是用于文件,将暂存区的文件unstage,将修改还原到工作区,相当于git add的反操作,如:
     git reset test.go
    

如果本地库和远程库的版本保持一致,那么因为reset之后本地库落后于远程库一个版本,需要使用-f参数强制提交,才能推送到远程库。

    git reset --hard HEAD^
    git push origin master -f

SSH方式访问github

  • 创建SSH Key。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa和id_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:

    $ ssh-keygen -t rsa -C "your-email@example.com"

  • 一路回车,使用默认值即可,由于这个Key也不是用于军事目的,所以也无需设置密码。如果成功,会在主用户目录下生成加密文件。

  • 登陆GitHub,打开“Account settings”,“SSH Keys”页面。然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容:

  • 点击Add key添加ssh key。一个账号可以添加多个key,用于在ssh方式访问github服务器时使用。

    ssh方式访问速度快,不需要每次访问时认证。

  • 检查SSH key是否有效,在git bash中输入: ssh -T git@github.com 会出现如下的提示信息:

    The authenticity of host 'github.com (IP ADDRESS)' can't be established. RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48. Are you sure you want to continue connecting (yes/no)?

    输入yes,如果成功会出现如下提示信息:

    Hi username! You've successfully authenticated, but GitHub does not provide shell access.

    如果失败,则根据对应的信息解决。

  • 对于HTTPS clone的项目,提交时不会使用SSH 方式。这就需要修改.git目录下的config文件,将url改为如下的git格式:

    url = git@github.com:akmumu/retina-data.git

清除历史提交记录

有时候在历史中提交了密码等敏感信息,或者历史过多,那么,我们需要清楚提交历史。

  • 创建独立的新分支
    git checkout --orphan <branch_name>
    

    –orphan 选项,创建一个独立分支并切换到该分支,该分支没有历史提交记录。

  • 将文件进行提交
    git add .
    git commit -m "<message>"
    
  • 删除需要清除历史记录的分支,并将当前分支名修改为该分支
    git branch -D master
    git branch -m master
    
  • 关联本地分支到远程
    git branch --set-upstream-to=origin/master
    
  • 将本地更改推送到远程分支
    git push -f origin master
    

错误以解决方法

index file smaller than expected

因为索引文件已经毁坏,我们可以重新建立。首先是移除它,然后重新添加到工作区

    rm .git/index
    git add .