git


Git远程仓库

komantao 2020-01-06 2020-01-06 字数 13065 阅读量


Git没有中心服务器,Git命令大部分是在本地执行,如果想通过Git分享代码或者与其他开发人员合作,就需要将数据放到一台其他开发人员能够连接的服务器上,这台服务器称为远程仓库。远程仓库是指托管在因特网或其他网络中的项目版本库。例如使用了GitHub作为远程仓库。

一、概述

远程仓库(又称远程主机)操作命令示意图:

远程仓库

  • 克隆:git clone 远程仓库URL 本地目录名

    将远程仓库复制到本地,同时检出远程默认分支到本地分支。

  • 获取:git fetch 远程仓库别名 +远程分支名:本地分支名

    将远程仓库的更新(若有)复制到本地仓库,不合并到本地分支。

  • 拉取:git pull 远程仓库别名 +远程分支名:本地分支名

    将远程仓库的更新(若有)复制到本地仓库,同时将远程分支合并到本地分支。

    git pull相当于git fetch + git merge FETCH_HEAD,手动解决合并冲突。

  • 推送:git push 远程仓库别名 +本地分支名:远程分支名

    将指定的本地分支复制到远程分支。

二、命令操作

1、新建远程仓库

参阅:GitHub新建仓库

假设新建了一个远程仓库(gitee.com/用户名/仓库名,示例使用Gitee,是因为克隆时下载速度极快)有3个分支:master分支(active branch活动分支,默认分支,可在远程仓库中自由设置默认分支)、new分支、aaa分支。

2、克隆远程仓库

使用git clone命令从现有Git远程仓库中拷贝默认分支到本地:

git clone <repository> <directory>
  • repository:Git远程仓库的git地址(url),包含传输协议,.git后缀可省略

  • directory:本地指定的目录,缺省时,克隆到当前目录

    • directory的名称为Git远程仓库项目在本地的新名称

    • directory必须为空目录,否则会触发错误:

      fatal: destination path '.' already exists and is not an empty directory.
      

2.1 克隆过程

将远程仓库克隆到本地(重命名为123,在本地检出远程默认分支master):

$ git clone git@gitee.com:用户名/仓库名.git 123
Cloning into '123'...
remote: Enumerating objects: 573, done.
remote: Counting objects: 100% (573/573), done.
remote: Compressing objects: 100% (427/427), done.
remote: Total 573 (delta 123), reused 529 (delta 102)
Receiving objects: 100% (573/573), 11.31 MiB | 7.92 MiB/s, done.
Resolving deltas: 100% (123/123), done.

克隆(git clone)操作的模拟分拆过程:

  1. 在本地硬盘内新建一个本地仓库

    1. 执行了mkdir 123

      在当前目录新建一个123目录,若缺省,则命名默认为远程仓库名。

    2. 执行了git init

      $ git init
      Initialized empty Git repository in xxx/123/.git/
      

      123目录新建一个空白本地仓库,git init命令默认创建了一个空白本地分支master。空白本地分支的概念是指:分支没有内容(任何提交)、.git/config文件没有关联远程分支、.git/refs/heads目录内没有索引。

  2. 本地仓库关联远程仓库

    执行了git remote add origin 远程仓库URL

    xxx/123 (master)
    $ git remote add origin git@gitee.com:用户名/仓库名.git
    

    本地仓库添加(关联)远程仓库,即本地仓库与远程仓库建立了跟踪关系。

    只是在配置文件(.git/config)中添加设置:

    [remote "origin"]                                 # 远程仓库origin
        url = git@gitee.com:用户名/仓库名.git         # 远程仓库的git地址
        fetch = +refs/heads/*:refs/remotes/origin/*   # fetch命令的src:dst
    
  3. 将远程仓库复制到本地仓库,并检出远程跟踪分支(远程默认分支)

    执行了git pull(参阅:拉取所有分支),只是在生成文件方面有些许差异:

    • git clone命令生成).git/packed-refs文件存储可关联的远程分支:

      # pack-refs with: peeled fully-peeled sorted 
      058b0101d4b3a1426242a0d204d6b00debf8b776 refs/remotes/origin/master
      76ae2d685b6f5c7a3aba314a15708bd066c2c1c1 refs/remotes/origin/new
      
    • .git/refs/remotes目录存储远程仓库的HEAD文件(指向远程默认分支):

      ref: refs/remotes/origin/master
      

2.2 检出默认分支

克隆远程仓库的所有分支,检出远程默认分支:

# 语法:git clone 远程仓库URL
git clone xxx.git

2.3 检出指定分支

克隆远程仓库的所有分支,检出远程指定分支:

# 语法:git clone -b 远程分支名 远程仓库URL
# 或:git clone -b 远程分支名 --single-branch 远程仓库URL
git clone -b [branch_name] xxx.git

2.4 指定克隆位置

将远程仓库克隆到本地硬盘的指定目录(可实现远程仓库名重命名):

# 语法:git clone 远程仓库URL 本地目录名
git clone xxx.git 目录名

2.5 指定克隆深度

控制克隆深度(即只克隆最后的n次提交):

# 语法:git clone --depth n 远程仓库URL
git clone --depth 10 xxx.git  # 克隆从最新开始的10次提交,剩余的不克隆

3、传输协议

参考:Git协议深度解析

远程仓库的url,可采用不同的传输协议。Git支持多种传输协议:

  • SSH协议用于需要用户认证的场合
  • HTTP(S)协议架设最简便(最大的麻烦是每次推送都必须输入账号和密码)
  • Git协议下载速度最快
  • FTP(S)协议低效,已弃用

Git远程仓库的url根据搭建时的规则所制定,上述协议不一定全适用(常用的有SSH和HTTPS)或使用其它协议。

3.1 SSH协议

SSH(Secure Shell)是一种网络协议,用于计算机之间的加密登录。是最常用的唯一一个同时支持读写操作的网络协议。

使用冒号表示隐式使用SSH协议:git@远程服务器URL:用户帐号/远程仓库名.git

$ git clone git@github.com:用户名/仓库名.git

显式指定SSH协议(没有冒号):ssh://git@远程服务器URL/用户帐号/远程仓库名.git

$ git clone ssh://git@github.com/用户名/仓库名.git

若显式时不增加SSH端口号,则会报错:

$ git clone git@github.com/用户名/仓库名.git
fatal: repository 'git@github.com/xxx/xxx.git' does not exist

备注:SSH协议是一个需要验证授权的网络协议,使用前需要配对SSH传输密钥(本地设置SSH密钥,远程服务器的SSH key列表添加SSH公钥)。因为Git托管平台(例如:GitHub)需要识别是谁提交了推送(防止别人冒充):GitHub匹配了SSH公钥,就默认用户正确。

创建SSH keys参阅:SSH keys

3.2 HTTP(S)协议

HTTP(S)协议的优美之处在于架设的简便性,但速度较慢,最大的麻烦是每次推送都必须输入账号和密码。在某些只开放http端口的公司(无法使用SSH协议)只能用HTTPS协议。

显式指定HTTPS协议:https://远程服务器URL/用户帐号/远程仓库名.git

$ git clone https://github.com/用户名/仓库名.git

使用HTTPS协议时:

  • 若远程仓库是公开的,克隆时不会弹出窗口要求输入账号和密码,直接下载成功

  • 若远程仓库是私有的(假设属于自己或有权限),克隆时Windows经常不会弹出窗口要求输入账号和密码(注意杀毒软件设置不要拦截弹出窗口),导致触发错误:

    remote: You do not have permission to pull from the repository via HTTPS
    fatal: Authentication failed for 'https://github.com/xxx/xxx.git/'
    

    改用SSH协议可克隆成功,因为本地仓库和远程仓库之间已配对了SSH密钥。

3.3 GIT协议

GIT协议是一个包含在Git软件包中的特殊守护进程,它会监听一个提供类似于SSH服务的特定端口(9418),而无需任何授权。

语法:git://git.远程服务器URL/用户帐号/远程仓库名.git

$ git clone git://git.github.com/用户名/仓库名.git

3.4 FTP(S)协议

所谓的远程仓库在FTP(S)协议中的表示,就是硬盘上的另一个目录。这常见于团队每一个成员都对一个共享的文件系统拥有访问权。

如果使用一个共享的文件系统,就可以在一个本地文件系统中克隆、推送和获取仓库。克隆时,只需要将远程仓库的路径作为URL使用。ftp和ftps可用于获取,但这是低效的并且已弃用;请勿使用。

语法:ftps://远程服务器/共享文件系统的路径.git

$ git clone ftps://host.xz[:port]/path/to/project.git

4、查看远程仓库

git remote命令查看本地仓库关联的远程仓库(显示别名):

$ git remote
origin

git remote -v命令查看本地仓库关联的远程仓库(显示别名和URL):

$ git remote -v
origin  git@github.com:用户名/仓库名.git (fetch)
origin  git@github.com:用户名/仓库名.git (push)
origin  git@gitee.com:用户名/仓库名.git (push)

git remote show命令查看本地仓库关联的远程仓库(显示别名、URL、分支等):

$ git remote show origin
* remote origin
  Fetch URL: git@github.com:用户名/仓库名.git
  Push  URL: git@github.com:用户名/仓库名.git
  Push  URL: git@gitee.com:用户名/仓库名.git
  HEAD branch: master
  Remote branch:
    master tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local ref configured for 'git push':
    master pushes to master (up to date)

git branch -a命令查看本地仓库的所有分支(显示本地分支和远程跟踪分支):

$ git branch -a
* master
  temp
  remotes/origin/master

5、关联远程仓库

本地仓库添加(关联)远程仓库:

git remote add [shortname] [url]
  • shortname:远程仓库的别名,方便将来引用。习惯使用origin
  • url:远程仓库的git地址,包含传输协议,.git后缀可省略

只是在配置文件(.git/config)中添加设置:

[remote "origin"]
	url = git@github.com:用户名/仓库名.git
	fetch = +refs/heads/*:refs/remotes/origin/*

之后执行push/pull操作后,远程分支才正式在本地仓库创建远程跟踪分支。然后,使用git branch -a命令可显示出远程跟踪分支。

6、关联远程分支

6.1 分支类型

分支的类型有:

  • 本地分支:在本地仓库内创建的分支

    .git/refs/heads目录存储本地分支的引用(reference,指向最后提交的版本)。

  • 远程分支:在远程仓库内创建的分支

    在远程仓库的code(代码)界面可查看远程分支的名称和内容。远程分支的提交历史存放在远程仓库内。Git托管平台只能显示出远程分支最后版本的内容,不能显示远程仓库。

  • 远程跟踪分支(分支跟踪关系,track)

    在本地分支与远程分支之间人为设置某种对应的跟踪关系,称为远程跟踪分支。

    • .git/refs/remotes/远程仓库名目录存放某个远程仓库的所有远程跟踪分支

    • 远程跟踪分支的作用

      • 本地分支和远程分支建立关联关系

        如果本地分支和远程分支建立了关联关系,当对本地分支进行push或pull时,Git会知道推送(push)到哪个远程分支,或者从哪个远程分支拉取(pull)到本地分支。

      • 在本地仓库内记录最后一次跟踪时的版本

        无论本地分支或远程分支的版本如何更新变化,在未执行命令(pull、push)同步时,远程跟踪分支依旧保持最后一次跟踪时的版本。

        在执行命令(pull、push)时,Git根据远程跟踪分支记录的版本、本地分支版本和远程分支版本三者的变化情况,同步本地分支或远程分支。最后,远程跟踪分支更新跟踪版本。

Git切换分支(git checkout 分支名)时:

  • 首先在refs/heads目录内查看本地分支

    若有,则切换到本地分支。

  • 然后在refs/remotes目录内查找远程跟踪分支

    若有,则将远程跟踪分支检出(新建)为本地分支。

    例外情况:git clone时,在refs/remotes/远程仓库别名目录内只有一个HEAD文件,没有远程跟踪分支,此时,可在packed-refs文件中查找。

使用git branch -a可查看本地仓库内的所有分支(本地和远程跟踪分支):

$ git branch -a
* master                        # 本地分支(前有*号,表示当前分支)
  temp                          # 本地分支
  remotes/origin/master         # 远程跟踪分支

使用git branch -r可查看本地仓库内的远程跟踪分支:

$ git branch -r
  origin/master

使用git remote -v可查看本地仓库关联的远程仓库:

$ git remote -v
gitee   git@gitee.com:用户名/仓库名.git (fetch)
gitee   git@gitee.com:用户名/仓库名.git (push)
origin  git@github.com:用户名/仓库名.git (fetch)
origin  git@github.com:用户名/仓库名.git (push)

6.2 upstream

跟踪关系(track)的一个重要概念是upstream/downstream。

Git是一个DVCS(Distributed Version Control System,分布式版本控制系统),在系统中没有固有的upstream(上游)和downstream(下游)。

upstream/downstream概念是相对于两个存储库,并且取决于数据流动的方式,目的是用来描述分支之间的相对位置,相当于族谱系统中的祖先ancestor/父母parent/孩子child/后代descendant之间的关系。除非本地仓库关联了对应的远程仓库,否则,您不会知道downstream是哪一个。

假设本地仓库关联了远程仓库:

  • pull时(pulling from upstream),upstream是远程分支,数据从远程仓库顺流到本地仓库
  • push时(pushing to upstream),upstream仍是远程分支,数据从本地仓库回流到远程仓库

简单一句话:远程仓库是本地仓库的upstream,本地仓库是远程仓库的downstream。

upstream(tracking)分支和远程跟踪分支(track)的区别:

  • 远程跟踪分支是指分支的一种跟踪关系(track)

    在本地分支与远程分支之间人为设置某种跟踪的对应关系。

    一个本地分支可以跟踪多个远程分支;一个远程分支也可以被多个本地分支跟踪。

  • upstream分支是指正在持续跟踪中(tracking)的远程分支

    从众多的跟踪关系中,人为指定一条表示正在跟踪中(tracking)。其中的远程分支称为upstream分支,对应的本地分支称为downstream分支。

设置upstream分支的目的:

  • 设置默认值,在使用pull、push时省略输入分支名(甚至远程仓库名)

  • 若pull/push其它远程分支,则需要写全分支名

    例如:git push 远程仓库名 本地分支名:远程分支名

6.3 设置upstream

从众多的远程跟踪分支(分支跟踪关系,track)中选择一个定义为正在持续跟踪中(tracking),对应的远程分支称为upstream分支,对应的本地分支称为downstream分支。

一个本地分支可以有多个upstream分支,但在配置文件(.git/config)记录中,一个本地分支只显示出一个upstream分支(运行命令设置新的upstream分支时,将会覆盖旧记录)。

设置upstream的方法有:

  • git clone 远程仓库URL

    本地分支(downstream分支)设置了upstream分支(远程默认分支),分支名称同名。

  • git pull + git checkout 分支名

    git pull命令创建远程跟踪分支,git checkout 分支名在检出分支内容时,本地分支设置了upstream分支,分支名称同名。

  • git pull --set-upstream 远程仓库名 远程分支名:本地分支名

    通过--set-upstream参数,运行pull时,本地分支设置了upstream分支。

  • git push -u|--set-upstream 远程仓库名 本地分支名:远程分支名

    通过-u|--set-upstream参数,运行push时,本地分支设置了upstream分支。

  • git branch –-set-upstream-to=远程仓库名/远程分支 本地分支

    通过--set-upstream-to=远程分支参数,本地分支设置了upstream分支。

    语法:git branch –-set-upstream-to=远程仓库名/远程分支 本地分支
    $ git branch --set-upstream-to=origin/new dev
    Branch 'dev' set up to track remote branch 'new' from 'origin'.
    
    1. 要求本地分支已存在(有内容)

      不能是空白分支,否则触发错误:

      $ git branch --set-upstream-to=origin/master master
      fatal: branch 'master' does not exist
      

      若不存在,请先新建本地分支:

      $ git branch 本地分支名
      
    2. 要求已在远程仓库创建了远程分支,在本地存有远程跟踪分支

      若远程跟踪分支不存在,将会触发错误:

      $ git branch --set-upstream-to=gitee/222 master
      error: the requested upstream branch 'gitee/222' does not exist
      hint:
      hint: If you are planning on basing your work on an upstream
      hint: branch that already exists at the remote, you may need to
      hint: run "git fetch" to retrieve it.
      hint:
      hint: If you are planning to push out a new local branch that
      hint: will track its remote counterpart, you may want to use
      hint: "git push -u" to set the upstream config as you push.
      
    3. 运行命令后,在配置文件(.git/config)中添加设置:

      [branch "dev"]                # 本地分支dev
         remote = origin           # 远程仓库origin
         merge = refs/heads/new    # 合并远程分支new到本地当前分支
      

设置upstream的关键步骤:查看FETCH-HEAD文件(pull时)或packed-refs文件(clone时)是否有可关联的远程分支。若有,则会在refs/remotes/远程仓库别名目录内生成远程跟踪分支。

设置upstream成功后,在配置文件(.git/config)中添加设置:

[branch "master"]                # 本地分支master
    remote = origin              # 远程仓库origin
    merge = refs/heads/master    # 合并远程分支master到本地当前分支

7、获取远程仓库

运行git fetch命令,是将一个远程仓库的更新(若有)拉取到本地仓库,不会合并分支。

git fetch [<options>] [<repository> [<refspec>]]

git fetch的执行过程:

git fetch origin develop:tmp    # 拉取远程develop分支,并放到本地tmp分支上
git diff tmp                    # 查看当前分支和tmp分支的区别
git merge tmp                   # 把tmp分支合并到当前分支
git branch -d tmp               # 删除tmp分支

合并阶段,使用命令:

  • git checkout 远程分支名:新建本地分支并切换(检出内容)

    仅适用于本地分支空白(即本地没有同名分支)时:

    $ git checkout master
    

    若使用git checkout 远程仓库名/远程分支名,将会进入“detached HEAD”状态。

  • git merge 分支名:将指定分支合并到本地当前分支

    适用于任何合并时:

    • 本地分支空白时(即本地没有同名分支)

      分支名的格式为:远程跟踪分支名(远程仓库名/远程分支名):

      $ git merge origin/master
      
    • 本地有同名分支时(已在该分支)

      分支名的格式为:远程分支名。

      $ git merge master
      

8、拉取远程仓库

运行git pull命令,将远程仓库的更新(若有)复制到本地仓库,然后指定的远程分支合并到对应的downstream分支(本地分支,若无,则新建同名分支)。git pull相当于 git fetch + git merge FETCH_HEAD

更确切地说,git pull是使用给定的参数运行git fetch,然后调用git merge将远程分支的HEAD(.git/FETCH_HEAD文件)合并到当前本地分支中。若使用--rebase,它将运行git rebase而不是git merge

git pull [<options>] [<repository> [<refspec>]]
  • repository:远程仓库别名或URL,缺省时使用默认值(git remote add关联的仓库)

  • refspec:格式为+<src来源地>:<dst目的地>,在pull时为远程分支:本地分支

    • +号:合并时使用非快进(non-fast-forward)方法,相当于-f参数(强制覆盖)

    • src:来源地(from,源引用)

      已有的引用,名称可以是任意“SHA表达式”,例如:master~4HEAD或分支名。

      省略src:dst)时,表示源引用为空白。等效于-d参数(删除目标引用)。

    • 冒号:表示命令操作方向(从左到右),from src:to dst

    • dst:目的地(to,目标引用)

      被命令操作的引用,名称只能为分支名。

      省略**:dst**时,表示目标引用与源对象引用同名。

    • 若src和dst的分支名不同时,将会使用非快进(non-fast-forward)方法合并

用法:

  • git pull
    或
    git pull repository
    

    拉取远程仓库所有远程分支的更新到本地,不合并到本地当前分支,不设置upstream。

    .git/FETCH_HEAD文件包含所有可跟踪的远程分支,显示“not-for-merge”:

    058b0101d4b3a1426242a0d204d6b00debf8b776	not-for-merge	branch 'aaa' of gitee.com:用户名/仓库名
    058b0101d4b3a1426242a0d204d6b00debf8b776	not-for-merge	branch 'master' of gitee.com:用户名/仓库名
    76ae2d685b6f5c7a3aba314a15708bd066c2c1c1	not-for-merge	branch 'new' of gitee.com:用户名/仓库名
    

    .git/refs/remotes/远程仓库别名目录存放远程跟踪分支。

  • git pull repository refspec
    

    拉取远程仓库所有远程分支的更新到本地,合并到本地分支,不设置upstream。

    .git/FETCH_HEAD文件初始只包含指定的远程分支:

    058b0101d4b3a1426242a0d204d6b00debf8b776		branch 'master' of gitee.com:用户名/仓库名
    

    静待一段时间后(期间无任何操作),.git/FETCH_HEAD文件变化为:

    058b0101d4b3a1426242a0d204d6b00debf8b776	not-for-merge	branch 'aaa' of gitee.com:用户名/仓库名
    058b0101d4b3a1426242a0d204d6b00debf8b776	not-for-merge	branch 'master' of gitee.com:用户名/仓库名
    76ae2d685b6f5c7a3aba314a15708bd066c2c1c1	not-for-merge	branch 'new' of gitee.com:用户名/仓库名
    
  • git pull --set-upstream repository refspec
    

    拉取远程仓库所有远程分支的更新到本地,合并到本地分支,设置upstream。

    .git/FETCH_HEAD文件包含所有可跟踪的远程分支:

    058b0101d4b3a1426242a0d204d6b00debf8b776		branch 'master' of gitee.com:用户名/仓库名
    058b0101d4b3a1426242a0d204d6b00debf8b776	not-for-merge	branch 'aaa' of gitee.com:用户名/仓库名
    76ae2d685b6f5c7a3aba314a15708bd066c2c1c1	not-for-merge	branch 'new' of gitee.com:用户名/仓库名
    

    在配置文件(.git/config)中添加设置:

    [branch "master"]                # 本地分支master
      remote = origin              # 远程仓库origin
      merge = refs/heads/master    # 合并远程分支master到本地当前分支
    

详细参数参阅官网文档

8.1 拉取所有分支

git pull命令的拉取过程:

  • 前提

    1. 要求已有本地仓库(git clone命令是自动新建本地空白仓库)

      • 若本地仓库为空白,则将远程仓库整体复制到本地仓库
      • 若本地仓库不为空白,则将远程仓库的更新(远程有,本地无)复制到本地仓库
    2. 要求本地仓库已关联远程仓库

      $ git remote add origin git@gitee.com:用户名/仓库名.git
      

      .git/config文件记录了仓库的关联关系:

      [remote "origin"]                                 # 远程仓库的别名为origin
         url = git@gitee.com:用户名/仓库名.git          # 远程仓库的git地址
         fetch = +refs/heads/*:refs/remotes/origin/*   # git fetch命令的src:dst
      
  • 第一阶段:将一个远程仓库的更新(若有)拉取到本地仓库

    假设本地当前分支尚未跟踪远程分支。

    运行git pull命令,将远程仓库的更新(远程有,本地无)复制到本地仓库,然后提示“当前分支没有正在跟踪信息”(There is no tracking information for the current branch.),但已将可关联的远程分支存放入FETCH-HEAD文件。

    $ git pull
    remote: Enumerating objects: 573, done.
    remote: Counting objects: 100% (573/573), done.
    remote: Compressing objects: 100% (427/427), done.
    remote: Total 573 (delta 124), reused 529 (delta 102)R
    Receiving objects: 100% (573/573), 11.31 MiB | 5.43 MiB/s, done.
    Resolving deltas: 100% (124/124), done.
    From gitee.com:用户名/仓库名
     * [new branch]      aaa        -> origin/aaa
     * [new branch]      master     -> origin/master
     * [new branch]      new        -> origin/new
    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=origin/<branch> master
    

    拉取后,可在本地仓库(.git目录)查看:

    1. .git/objects/pack/pack-xxx.pack二进制文件

      存储了远程仓库所有分支的数据对象文件。

    2. .git/FETCH-HEAD文件

      存储了可关联的远程分支(存储最后一次git pull的结果)。示例:

      058b0101d4b3a1426242a0d204d6b00debf8b776	not-for-merge	branch 'aaa' of gitee.com:用户名/仓库名
      058b0101d4b3a1426242a0d204d6b00debf8b776	not-for-merge	branch 'master' of gitee.com:用户名/仓库名
      76ae2d685b6f5c7a3aba314a15708bd066c2c1c1	not-for-merge	branch 'new' of gitee.com:用户名/仓库名
      

      .git/ORIG_HEAD文件存储了FETCH-HEAD文件的上一次结果,作用是在进行危险操作时,提供回退功能。

    3. .git/refs/remotes/远程仓库别名目录

      因为FETCH-HEAD文件已有可关联的远程分支,所以该目录存储了所有远程跟踪分支的索引(upstream分支最后一次提交的哈希值)。

  • 第二阶段:将upstream分支合并到本地当前分支

    运行git checkout 分支名,自动设置upstream分支和检出分支内容:

    xxx/123 (master)
    $ git checkout master
    Already on 'master'
    Branch 'master' set up to track remote branch 'master' from 'origin'.
    
    1. 设置upstream分支

      设置upstream的方法参阅:设置upstream的方法

      设置upstream成功后,在配置文件(.git/config)中添加设置:

      [branch "master"]                # 本地分支master
         remote = origin              # 远程仓库origin
         merge = refs/heads/master    # 合并远程分支master到本地当前分支
      

      .git/FETCH-HEAD文件变化为:

      058b0101d4b3a1426242a0d204d6b00debf8b776		branch 'master' of gitee.com:用户名/仓库名
      058b0101d4b3a1426242a0d204d6b00debf8b776	not-for-merge	branch 'aaa' of gitee.com:用户名/仓库名
      76ae2d685b6f5c7a3aba314a15708bd066c2c1c1	not-for-merge	branch 'new' of gitee.com:用户名/仓库名
      

      设置后,才能将远程分支的内容合并到本地分支。

    2. 将远程跟踪分支的更新内容合并到本地当前分支

      若有冲突,则需要协商手动解决合并冲突。

      合并后(本地分支有提交记录了):

      • 本地目录显示出本地分支的内容(与远程分支在远程仓库内显示的内容一致)
      • .git/refs/heads目录显示出本地分支的索引
  • 第三阶段:切换到其它远程跟踪分支(查看其它远程分支的内容)

    查看FETCH-HEAD文件是否已有可关联的远程分支(关键因素):

    • 若有

      • 方法一:建立upstream/downstream,不会丢失程分支的提交历史

        直接运行git checkout 分支名,检出远程跟踪分支到本地分支(Git会自动新建本地分支,与远程分支名同名,然后建立关联,最后检出分支内容)。

        $ git checkout new
        Switched to a new branch 'new'
        Branch 'new' set up to track remote branch 'new' from 'origin'.
        
      • 方法二:手动新建一个本地分支,建立upstream/downstream,不会丢弃了远程分支的提交历史

        1. 运行git checkout 分支名

          新建一个本地分支bbb(.git/refs/heads目录内有显示):

          xxx/123 (master)
          $ git branch bbb
          

          在本地当前分支master分叉出本地分支bbb,.git/refs/heads/bbb显示:

          058b0101d4b3a1426242a0d204d6b00debf8b776
          
        2. 运行git branch --set-upstream-to=upstream branchname

          xxx/123 (master)
          $ git branch --set-upstream-to=origin/new bbb
          Branch 'bbb' set up to track remote branch 'new' from 'origin'.
          

          在配置文件(.git/config)中添加设置:

          [branch "bbb"]                # 本地分支bbb
             remote = origin           # 远程仓库origin
             merge = refs/heads/new    # 合并远程分支new到本地当前分支
          
        3. 运行git pull

          若直接运行git checkout bbb,由于bbb分支已有内容(且是不同分支的不同内容),所以触发错误:

          xxx/123 (master)
          $ git checkout bbb
          Switched to branch 'bbb'
          Your branch is behind 'origin/new' by 22 commits, and can be fast-forwarded.
            (use "git pull" to update your local branch)
          

          运行git pull,使用Fast-forward方式合并:

          xxx/123 (bbb)
          $ git pull
          Updating 058b010..76ae2d6
          Fast-forward
          …………
          

          合并后,.git/FETCH-HEAD文件变化为:

          76ae2d685b6f5c7a3aba314a15708bd066c2c1c1		branch 'new' of gitee.com:koman/koman
          058b0101d4b3a1426242a0d204d6b00debf8b776	not-for-merge	branch 'aaa' of gitee.com:koman/koman
          058b0101d4b3a1426242a0d204d6b00debf8b776	not-for-merge	branch 'master' of gitee.com:koman/koman
          

          合并后,.git/refs/heads/bbb显示:

          76ae2d685b6f5c7a3aba314a15708bd066c2c1c1
          
      • 方法三:手动新建一个本地空白分支,不会建立upstream/downstream,丢弃了远程分支的提交历史

        1. 运行git checkout 远程跟踪分支名

          进入“detached HEAD”状态,除了在工作目录显示出远程跟踪分支的检出内容外,其它数据没有变化。

          xxx/123 (master)
          $ git checkout origin/aaa
          Note: switching to 'origin/aaa'.
                   
          You are in 'detached HEAD' state.
          …………
          
        2. 运行git checkout --orphan 分支名

          在“detached HEAD”状态下新建一个空白的本地分支abc(.git/refs/heads目录内无显示):

          xxx/123 ((058b010...))
          $ git checkout --orphan abc
          Switched to a new branch 'abc'
          
          • 运行后,Git自动退出“detached HEAD”状态,切换到本地分支abc
          • 远程跟踪分支aaa的检出内容保留在工作目录
          • 本地分支abc下的所有文件的状态为已暂存未提交
        3. 提交本地分支abc下的内容

          $ git commit -m "aaa"
          [abc (root-commit) f64e275] aaa
           425 files changed, 73625 insertions(+)
           create mode 100644 404.html
           …………
          

          本地分支abc是一个独立分支,不再是从远程分支分叉出来的分支,即已丢弃了远程分支的提交历史。

    • 若无

      则无法切换。可再次运行git pull,尝试添加远程跟踪分支。

8.2 拉取指定分支

拉取指定远程分支,使用--set-upstream参数设置upstream分支,同时检出分支:

$ git pull --set-upstream origin master
remote: Enumerating objects: 529, done.
remote: Counting objects: 100% (529/529), done.
remote: Compressing objects: 100% (383/383), done.
remote: Total 529 (delta 102), reused 529 (delta 102)
Receiving objects: 100% (529/529), 11.30 MiB | 2.87 MiB/s, done.
Resolving deltas: 100% (102/102), done.
From gitee.com:用户名/仓库名
 * branch            master     -> FETCH_HEAD
 * [new branch]      master     -> origin/master
  • 在本地目录显示出指定远程分支master的内容

  • .git/objects/pack/pack-xxx.pack二进制文件存储所有远程分支的数据对象文件

  • .git/config文件添加了upstream分支的设置

    [branch "master"]
      remote = origin
      merge = refs/heads/master
    
  • .git/FETCH-HEAD文件,初始只记录了指定远程分支master。但静待一段时间后(期间无任何操作),FETCH-HEAD文件自动更新包含所有远程分支(原因未明)。

    058b0101d4b3a1426242a0d204d6b00debf8b776		branch 'master' of gitee.com:用户名/仓库名
    
  • .git/refs/remotes目录只存储了指定远程跟踪分支master。但静待一段时间后(期间无任何操作),refs/remotes目录自动更新包含所有远程分支(原因未明)。

    058b0101d4b3a1426242a0d204d6b00debf8b776
    
  • .git/refs/heads目录存储了本地分支master的索引

    058b0101d4b3a1426242a0d204d6b00debf8b776
    

此时,若想切换到new分支,将会触发错误(因为本地仓库没有拉取远程分支new):

$ git checkout new
error: pathspec 'new' did not match any file(s) known to git

解决方法:运行git pull命令补全(拉取)所有远程分支

  1. 首先,运行git pull命令补全(拉取)所有远程分支

    $ git pull
    remote: Enumerating objects: 45, done.
    remote: Counting objects: 100% (45/45), done.
    remote: Compressing objects: 100% (44/44), done.
    remote: Total 44 (delta 21), reused 0 (delta 0)
    Unpacking objects: 100% (44/44), 4.78 KiB | 4.00 KiB/s, done.
    From gitee.com:koman/koman
     * [new branch]      aaa        -> origin/aaa
     * [new branch]      new        -> origin/new
    Already up to date.
    
  2. 然后,运行git checkout new命令检出指定分支

    $ git checkout new
    Switched to a new branch 'new'
    Branch 'new' set up to track remote branch 'new' from 'origin'.
    

9、推送到远程仓库

push.default参数的可选值:

  • nothing:push操作无效,除非显式指定远程分支
  • current:push当前分支到远程同名分支,如果远程同名分支不存在则自动创建同名分支
  • upstream:push当前分支到它的upstream分支上
  • simple:simple和upstream是相似的,只有一点不同,simple必须保证本地分支和它的远程upstream分支同名,否则会拒绝push操作(注意必须同名)
  • matching:push所有本地和远程两端都存在的同名分支

可以使用git config -l 查看配置,使用git config命令修改默认配置。

$ git config --global push.default matching
或
$ git config --global push.default simple

在Git_v_2.0后,push.default参数采用simple:即在运行git push命令,只是将本地指定(或当前)分支的更新(若有)复制(合并)到远程分支(若无,则新建同名分支)。

git push [<options>] [<repository> [<refspec>]]
  • repository:远程仓库别名或URL,缺省时使用默认值(git remote add关联的仓库)

  • refspec:格式为+<src来源地>:<dst目的地>,在push时为本地分支:远程分支

    refspec的格式描述参阅:拉取远程仓库

  • options

    参数 功能 备注
    –all 推送本地引用(refs/heads目录) 不能与refspec参数一起使用
    –mirror 镜像所有(refs目录,包括但不限于refs/headsrefs/remotesrefs/tags 新创建的本地引用将被推到远程端,本地更新的引用将在远程端强制更新,删除的引用将从远程端删除。
    –tags 推送tags(refs/tags目录)
    -d 从远程仓库中删除引用 等效于refspec参数设置为**:dst**(空白本地引用推送到远程引用)
    -f 强制推送(本地引用覆盖远程引用) 允许非快进(non-fast-forward)推送
    -u push成功后,设置upstream引用

备注:

  • push只是推送指定分支的更新,不是推送本地仓库的所有分支

  • 推送成功后,将会在本地创建远程跟踪分支(track)

  • 对远程仓库有写权限且远程分支的版本低于本地分支时,才能push成功

    若远程分支版本高于本地分支(已有其他人推送了若干更新),推送操作会被拒绝:

    xxx/123 (new)
    $ git push gitee new:aaa
    To gitee.com:用户名/仓库名.git
     ! [rejected]        new -> aaa (non-fast-forward)
    error: failed to push some refs to 'git@gitee.com:用户名/仓库名.git'
    hint: Updates were rejected because a pushed branch tip is behind its remote
    hint: counterpart. Check out this branch and integrate the remote changes
    hint: (e.g. 'git pull ...') before pushing again.
    hint: See the 'Note about fast-forwards' in 'git push --help' for details.
    

    必须先将更新抓取(git pull)到本地,然后才可以再次推送。

9.1 标准用法

  • git push
    

    表示将本地当前分支推送到配置文件(.git/config)记录的upstream分支。

  • git push 远程仓库名
    

    表示将本地当前分支推送到远程仓库的同名分支(若无,则在远程仓库新建远程分支)。

  • git push 远程仓库名 本地分支名:远程分支名
    

    表示将本地指定分支推送到远程仓库的指定远程分支:

    • 省略**:远程分支名**时,表示推送到同名的远程分支(若无,则新建)
    • 省略本地分支时,表示推送一个空的本地分支到远程分支,等效于删除远程分支
  • git push -u 远程仓库名 本地分支名:远程分支名
    

    -u参数,表示push时同步设置upstream分支。

  • git push --all 远程仓库名
    

    --all参数,表示将本地所有分支都推送到远程仓库的同名分支。

  • git push -f 远程仓库名 本地分支名:远程分支名
    

    -f参数,表示强制推送。除非你已确定要这样做,否则慎用-f选项。

    当本地分支版本低于远程分支版本时,push时会被拒绝(要求先pull远程分支,解决合并差异,然后再push到远程分支)。使用-f参数时,就会使用”非快进”(non-fast-forward)方式合并(覆盖)远程分支,忽略版本判断,强制覆盖远程分支。

  • git push --tags 远程仓库名
    

    push时默认不会推送标签,使用--tags选项推送标签。

9.2 push到多个仓库

使用一个push命令,将本地分支的更新同时推送到多个远程仓库:

  1. 添加(关联)远程仓库

    • 添加第一个远程仓库:git remote add 远程仓库名 远程仓库URL1
    • 添加第二个远程仓库:git remote set-url --add 远程仓库名 远程仓库URL2
    • 添加第三个远程仓库:git remote set-url --add 远程仓库名 远程仓库URL3

    备注:远程仓库名使用同一个名称。

    添加后,在配置文件(.git/config)内显示:

    [remote "origin"]
     url = git@github.com:用户名/仓库名.git                # 第一个远程仓库
     fetch = +refs/heads/*:refs/remotes/origin/*
     url = git@gitee.com:用户名/仓库名.git                 # 第二个远程仓库
    

    使用git remote -v命令,查看远程仓库信息:

    $ git remote -v
    origin  git@github.com:用户名/仓库名.git (fetch)
    origin  git@github.com:用户名/仓库名.git (push)
    origin  git@gitee.com:用户名/仓库名.git (push)
    
    • 可以有多个push-url(push到多个url),但只有一个fetch-url(只能pull一个url)
    • fetch-url默认为第一个远程仓库url,在配置文件(.git/config)内排在第一行。若想变更fetch-url,将远程仓库的url放在配置文件(.git/config)的第一行即可
  2. 正常使用push命令,就能实现同时推送到多个远程仓库

    git push 远程仓库名 本地分支名
    

感谢您的赞赏支持:

Copyright © 2020 komantao. All Rights Reserved.