Git

141 阅读15分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

git是目前世界上最先进的分布式版本控制系统(git-scm.com/doc)

git概念

工作区

进入监视

git clone / git init / git add / git fetch / git pull(push) / git checkout / git log(reflog) / git config / git status

缓存区stage

半控制

git reset / git checkout / git stash & apply / git rm / git status

远程仓库

全控制

git branch / git merge / git diff / git remote / git rebase / git reset

git常见使用场景

  1. git 如何把master分支代码合并到自己的分支

    1,先切换到主分支
        git checkout master
    2, 使用git pull把领先的主分支代码拉下来
        git pull
    3, 切换到自己的分支
        git checkout dev-01
    4, 把主分支的代码合并到自己的分支
        git merge master
    
  2. commit到本地仓库的代码,追加代码修改,最后一起push到远程仓库

    git add .
    git commit --amend
    

版本格式

主版本.次版本.修订号-beta(先行版本).1 + meta(元数据)

  • alpha - 内部测试版本,除非是内部测试人员,否则不推荐使用,有很多bug
  • beta - 公测版本,消除了严重错误,还是会有缺陷,这个阶段还会持续加入新功能
  • rc - release candidate,发行候选版本,这个版本不会加入新功能,主要是排错,修改bug
全局变量设置
git config --global user.name 'your name'
git config --global user.email 'your email'

配置的时候,加上--global是针对当前用户起作用的,如果不加,就只针对当前的仓库起作用

每个仓库的git配置文件都在.git/config文件中

当前用户的git配置文件放在用户主目录的隐藏文件.gitconfig中

密钥

ssh密钥

git使用https密钥,每次pull、push都要输入密码

使用git协议,然后使用ssh密钥,这样可以省去每次都输密码。需要三个步骤

  1. 本地生成密钥对
  2. 设置gitlab上的公钥
  3. 修改git的remote url为git协议
生成密钥

大多数git服务器都会选择使用SSH公钥来进行授权,系统中的每个用户都必须提供一个公钥用于授权。首先确认一下是否已经有一个公钥了,SSH公钥默认储存在账户的主目录下的~/.ssh目录,查看该目录下是否有id_rsa(密钥)和id_rsa.pub(公钥),如果没有,可以用ssh-keygen创建

添加公钥到git服务器

ssh -T <git@github.com>验证是否添加成功

修改本地的ssh remote url,改用git协议

git remote -v查看当前的remote url

登录git服务器,找到git协议的地址

使用git remote set-url origin git协议的地址

设置完成后,可以用git remote -v查看,已经切换为git协议了,不必再每次输入密码

本地多个SSH密钥文件

有时候可能多个云平台都要用到ssh key来认证,如果每次都覆盖原来的id_rsa文件,那么之前的认证就会失效。这时候我们可以通过在~/.ssh目录下增加config文件来解决

  1. 配置git用户名和邮箱

    git config user.name 'your name'
    git config user.email 'your email'
    
  2. 生成ssh key时同时指定保存的文件名

    ssh-keygen -t rsa -f ~/.ssh/id_rsa.company -C "email"
    

    id_rsa.company就是指定的文件名

    这时~/.ssh目录下就会多出id_rsa.company和id_rsa.company.pub两个文件,id_rsa.company.pub里保存的就是我们要使用的key

  3. 如果config文件不存在,先添加,存在则直接修改

    touch ~/.ssh/config
    

    在config文件里添加如下内容

    Host 域名或者IP
        IdentityFile ~/.ssh/id_rsa.company
        User 用户名
    
  4. 上传key到云平台后台

  5. 测试ssh key是否配置成功

    ssh -T git@域名 -p 端口号

  6. 这样便可以配置多个ssh key了

git 命令

git push

第一次使用push之前,需要对git push进行配置

  1. simple方式

    git config --global push.default.simple

    simple方式的push只会push已经从远程仓库pull过的分支,意思就是假如曾经pull了分支dev,那么当使用缺省git push时,当前分支为dev,远程分支dev就会收到commit

  2. matching方式

    git config --global push.default.matching

    matching方式的push会把所有本地的分支push到远程仓库中对应匹配的分支

  3. 使用git push [远程仓库][本地分支]

创建版本库

git init创建一个空的版本库,.git目录用来跟踪管理版本库

git status
git diff
获取远程仓库代码
  1. git clone <版本库的网址> <本地目录名>

  2. git remote add [远程仓库名][远程仓库地址],比如git remote add origin 仓库地址

    git要求每个远程主机都必须指定一个主机名,git remote命令就用于管理主机名

    git remote列出所有远程主机

    git remote -v可以查看远程主机的网址

    克隆版本库的时候,所使用的远程主机自动被git命名为origin,如果想用其他的主机名,需要用git clone命令的 -o选项指定

  3. 拉取代码;如果已经被git管理的项目,可以使用git pull和git fetch来管理代码的拉取和更新;

    • 使用git pull拉取远程代码的HEAD头标记,即最新的代码

      git pull <远程主机名> <远程分支名><本地分支名>
      
git add

把所有的文件更改提交到暂存区

git commit

把所有暂存区的代码写入日志并提交到本地仓库

git push

把所有本地仓库的提交,更新到远程仓库

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

git push <远程主机名> <本地分支名>:<远程分支名>
<来源地>:<目的地>
git pull 是<远程分支>:<本地分支>
git push 是<本地分支>:<远程分支>

如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支

git push origin :master
等同于
git push origin --delete master

如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略;如果当前分支只有一个追踪分支,那么主机名都可以省略,只用git push就可以了

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

将本地的master分支推送到origin主机,同时指定origin为默认主机,后面就可以直接用git push
git push -u origin master

另外一种matching方式,会推送所有有对应的远程分支的本地分支

还有一种情况,就是不管是否存在对应的远程分支,将本地的所有分支都推送到远程主机,这种情况要添加--all选项

git push --all origin

如果远程主机的版本比本地版本新,推送git会报错,要先在本地git pull合并差异,然后再推送到远程主机,如果添加--force选项,会导致远程主机上更新的版本被覆盖,应尽量避免使用--force选项。

git push --force origin

git push不会推送标签tag,除非使用--tags选项

git push origin --tags
git fetch

一旦远程主机的版本库有了新的commit,需要将这些更新取回本地,这时就可以用git fetch

git fetch <远程主机名>

上述命令可以将某个远程主机的更新,全部取回本地

默认情况下,git fetch取回所有分支的更新,如果只想取回特定分支的更新,可以指定分支名

git fetch <远程主机名><分支名>

比如git fetch origin master是取回origin主机的master分支

可以使用git merge或者git rebase命令,在本地分支上合并远程分支

git merge origin/master

git rebase origin/master

上述命令表示在当前分支上,合并origin/master

git pull

取回远程主机某个分支的更新,再与本地的指定分支合并

git pull <远程主机名> <远程分支名>:<本地分支名>

比如取回origin主机的next分支,与本地的master分支合并,写法为:

git pull origin next:master

如果远程分支是与当前分支合并,则冒号后面的分支可以省略

git pull origin next #取回origin/next分支,再与当前分支合并,等同于git fetch origin+ git merge origin/next
git追踪关系tracking

有时候,git会自动在本地分支与远程分支之间,建立一种追踪关系,比如在git clone的时候,所有本地分支默认与远程主机的同名分支,建立追踪关系,即本地的master分支自动追踪origin/master分支。如果当前分支与远程分支存在追踪关系,git pull就可以省略远程分支名;如果当前分支只有一个追踪分支,连远程主机名都可以省略。

如果合并需要采用rebase模式,可以使用--rebase选项

git pull --rebase <远程主机名> <远程分支名>:<本地分支名>

如果远程主机删除了某个分支,默认情况下,git pull不会在拉取远程分支的时候,删除对应的本地分支。可以通过添加-p参数改变这个默认行为

git pull -p
等同于
git fetch --prune origin
git fetch -p

git时光机

git log

查看每次修改的日志文件;git log是顺着当前分支前去查找提交记录;git reflog是作为本地提交记录的清单

git reset

在git中,HEAD表示当前版本,上一个版本就是HEAD^,上100个版本HEAD~100

git reset --hard HEAD^ 回退add命令,提交到缓存区的文件撤销掉,重新放回工作区

git reset HEAD file 提交到缓存区的文件撤销掉,重新放回工作区

git checkout

丢弃缓存区文件的修改,把文件恢复到git add之前的状态

标签管理

发布一个版本时,通常现在版本库中打一个标签,唯一确定打标签时刻的版本,标签属于版本库的一个快照。

实际就是指向某个commit的指针,但标签不可以移动,所以创建和删除标签都是瞬间完成的

注意:标签不是按照时间顺序列出,而是按照字母排序的

可以使用git show [tag name]查看标签信息

创建标签

首先,切换到需要打标签的分支上,使用git tag打一个新标签,也可以使用这个命令查看所有标签

git tag v1.0 <commit id>

默认标签是打在最新提交的commit上的,如果忘记给某次的commit打标签了,可以找到历史提交的commit id,然后打上标签

git tag -a [tag name] -m [tag info]

标签的删除与推送

git push origin可以推送一个本地标签

git push origin --tags可以推送全部未推送的本地标签

git push origin :refs/tags/可以删除一个远程标签,要先用git tag -d [tag name]删除本地标签,再用push删除远程标签

.gitignore忽略文件

一些必须放在工作目录中,但又不能提交的文件,比如数据库密码的配置文件等。在git工作区的目录下创建一个.gitignore文件,然后把要忽略的文件写进去,git提交的时候就会自动忽略这些文件。

忽略文件的原则是:

  • 忽略操作系统自动生成的文件,比如缩略图等
  • 忽略编译生成的中间文件、可执行文件等
  • 忽略带有敏感信息的配置文件
.gitignore规则不生效的解决办法
git rm -r --cached .
git add .
git commit -m 'update .gitignore'
配置别名
git config --global alias.lg "log --color --graph --pretty=format:'%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
​
执行git lg

Git分支管理

一开始的时候,HEAD头指针指向的是主分支,即master分支。而HEAD指向的是当前分支,master指向的是提交。如果,在master分支上新建一个分支dev,此时HEAD指向了dev,这时对工作区的修改和提交就是针对dev分支的了,比如新提交一次后,dev指针往前移动一步,而master指针不变。

git branch [分支名] #创建分支
git branch  #查看所有分支
git checkout [分支名] #切换到对应的分支
git branch -d [分支名] #删除分支
冲突解决

当两个分支中修改相同的文件并提交(add>commit),合并(merge)这两个分支的时候,会产生冲突。

通常,合并分支时,如果可能,git会用fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。如果要强制禁用fast forward模式,git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。

  1. 从master分支切换出一个新的分支

  2. 在新的分支上修改代码

  3. 切换回master分支,准备合并dev分支,使用--no--ff模式禁用fast forward

    git merge --no--ff -m'merge with no-ff' dev

分支策略

在实际开发中,我们应该按照几个基本原则进行分支管理

  1. master分支应该是非常稳定的,也就是仅仅用来发布新版本,平时不能在上面修改代码
  2. 修改代码应该都在dev分支上,即dev分支是不稳定的,等到功能都开发完成了,比如1.0版本发布的时候,再把dev分支合并到master上,在master分支发布1.0版本
  3. 开发的时候可以每个开发有自己的分支,然后经常往dev分支上合并就可以了
团队合作的分支

每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。比如修复一个代号为101的bug任务,可以新建一个issue-101分支来修复。如果这时在dev开发代码无法提交,可以先用git stash把现场的代码储藏起来,等把bug修复之后,再执行git stash pop把代码还原,继续进行dev的开发工作

如果要在master分支上修复bug,就从master创建临时分支,修复完成后,切换到master分支,并完成合并,最后删除issue-101分支

git checkout master
git checkout -b issue-101
#修改代码
git add [修改的文件名]
git commit -m'fix bug 101'
git checkout master
git merge --no--ff -m'merged bug fix 101' issue-101
git branch -d issue-101

#bug修复完成后,继续回到dev进行开发
git checkout dev
git stash pop #恢复代码的同时把stash里的内容也删除了

git stash apply恢复代码时,stash里的内容并不删除,需要用git stash drop来删除stash

git stash list 查看stash 列表

git stash apply stash@{0}恢复指定的stash代码

删除分支

每添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并。最后,删除该feature分支。feature分支与bug分支一般情况下是相似的。

git checkout -b feature-new
git add .
git commit 
git checkout dev
git merge --no--ff freature-new

如果突然要取消新开发的功能,需要立刻销毁新分支,分两种情况

  1. 如果没有合并之前,可以简单的使用git branch -d [分支名]来删除分支(使用-D命令,强制删除分支)
  2. 如果已经合并,除了上面的需要删除,还需要使用git reset --hard HEAD^来退回到上一个版本
多人协作

首先,可以试图用git push origin branch-name推送自己的修改;如果推送失败,则因为远程分支比本地的更新,需要先用git pull 试图合并,如果合并有冲突,就要先解决冲突,并在本地提交;没有冲突或解决掉冲突之后,再用git push origin branch-name推送就可以了。

如果git pull提示“no tracking information”则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream branch-name origin/branch-name

所有工作流建立再已经建立了个人账户,并添加了SSH key的个人文档中。

Git恢复流程

当中心仓库由于不可抗力因素跨了之后,项目的恢复流程有

恢复指定分支
  1. 注册账号 > 输入SSH keys > 新建项目

  2. 在原项目文件夹下,使用git remote -v命令查看;使用git remote remove origin删除原有仓库地址

  3. 使用新的仓库地址,git remote add origin [ssh仓库地址]

  4. 添加文件,commit提交,最后push上远程指定分支

    git add .
    git commit -m'add my repo'
    git push origin master #这条命令会把当前分支,推送到远程的master
    git push origin dev:dev #把本地的dev分支,推送到远程的dev分支
    

    git分支提交的问题:git push origin dev:dev 为什么dev后还要加一个冒号和dev呢

    git push <远程主机名> <本地分支名>:<远程分支名>

    前面的是本地分支名,后面的是远程分支名,同名可以省略冒号部分

    git push origin dev:devgit push origin dev 是等效的

恢复项目所有分支
git remote remove origin
git remote add origin [新的SSH仓库地址]
git push --mirror [旧的仓库地址]

\