Posts Git基本使用方法(三):远程仓库篇
Post
Cancel

Git基本使用方法(三):远程仓库篇

一、 前言

相信很多人接触并认识到git是因为Github

多人协同的场景、或者多个设备上开发的场景中, 一个远程的仓库作为”server”就十分必要了

Github正是为用户提供了这样的一个”server”

这一篇中我们跳出本地仓库, 学习和远程仓库的交互操作

二、 了解远程仓库

git是一个开源的分布式版本控制系统

虽然我们时常会设置一个中央服务器来托管代码, 但是抛开服务器的自身性质, 提供服务的远程仓库和本地仓库, 它们的本质是一样的——你可以建立一个本地的”远程库”, 进行和真正的”远程库”基本一样的操作

裸(bare)仓库简介

其实一般的远程仓库, 和本地仓库还是有一定区别的, 它们往往是bare的:

1
git init --bare (仓库名)

我们一般称之为”裸仓库”

被设计成远端的中心仓库, 这样的仓库没有工作树, 故大部分本地仓库的命令不适用于裸仓库

除了被砍掉工作树, “裸仓库”对于远程更新的行为(如push), 有更弱的防线

它成为了一个纯粹的仓库, 你并不能在其中部署生产环境, 那里只能托管代码

下面是bare仓库的根目录结构, 不同仓库有工作区, 所以我们可以查看和编辑代码, 但这里没有

ls -alh

我们push过去的代码放在objects/pack/目录下、以打包后的文件.idx&.pack的形式存在着

如果我们在服务器自建一个普通的仓库, 本地对其push操作后, 会收到远程仓库的拒绝, 它会提示你

1
2
3
4
5
6
7
8
9
10
11
12
13
14
refusing to update checked out branch: refs/heads/master
By default, updating the current branch in a non-bare repository
is denied, because it will make the index and work tree inconsistent
with what you pushed, and will require 'git reset --hard' to match
the work tree to HEAD.
remote: error:
You can set 'receive.denyCurrentBranch' configuration variable to
'ignore' or 'warn' in the remote repository to allow pushing into
its current branch; however, this is not recommended unless you
arranged to update its work tree to match what you pushed in some
other way.
remote: error:
To squelch this message and still keep the default behaviour, set
'receive.denyCurrentBranch' configuration variable to 'refuse'.

总结成两句话:

  • 默认情况下, 将当前的分支在一个non-bare的仓库中更新, 这样的操作会被拒绝
  • 解决办法是将远程仓库的receive.denyCurrentBranch设置为ignore或者warn, 但不推荐这样做

总结成一点就是: 推荐将远程仓库初始化为裸(bare)仓库

三、 基本操作

3.1 clone(克隆远程库)

这个操作将在当前目录中创建远程库的文件夹

1
git clone [仓库地址] (本地仓库名)
  • 仓库地址可以是http协议、git协议(git://)、也可以是ssh协议(例如root@server:/path/to/repo.git)、本地文件协议等等
  • 本地仓库名是可选的, 若不指定, 本地仓库名取决于远程仓库

3.2 remote(管理远程库)

在进行远程仓库的操作前, 我们需要给当前的操作配置”源”, 帮助git找到当前仓库的远程源

  • 如果当前库是clone得到的, 则被clone的地址就自动加到了源的列表中、并命名为origin
  • origin是默认的名字、也是大家约定俗成的命名方式

添加远程库

1
git remote add <源名称> <源url>

删除现有远程库

1
git remote remove <源名称>

重命名现有远程库

1
git rename <旧名称> <新名称>

查看现有远程库

1
git remote [-v]

-v选项即--verbose, 显示更详细的信息(包含url信息)

获取源url的方法, 除了git remote -v, 我们还可以使用

1
2
# 查
git remote get-url [--path] [--all] <源名称>

不加选项时默认返回fetch源, 可以用--push获得push源, 但一般情况下二者是一致的

与之对应的, 是修改源url的方法:

1
2
3
4
5
6
# 改
git remote set-url [--push] <源名称> <新url> [<旧url>]
# 增
git remote set-url --add [--push] <源名称> <新url>
# 删
git remote set-url --delete [--push] <源名称> <要删除的url>

3.3 fetch(获取远程库更新)

这个命令将远程仓库的最新内容拉到本地, 若不指定分支名, 则获取所有分支的更新

1
git fetch <源名称> [<远程分支名>]

取回所有远程分支的fetch命令实际上在本地创建了本地远程分支, 这种分支的命名默认采用源名称/远程分支名的方式, 例如origin/master

注意: 取回单个分支时不会创建本地远程分支

我们也可以在fetch单个分支时, 对其重命名

1
git fetch <源名称> <远程分支名>:<本地分支名>

在进行fetch操作后, 我们会得到一个FETCH_HEAD, 它不完全对应于本地的HEAD, 因为本地的HEAD始终指向当前分支, 而我们进行的fetch操作不一定会将其HEAD所指的分支取回

  • 如果没有显式的指定远程分支, 则远程分支的master将作为默认的FETCH_HEAD
  • 如果指定了远程分支, 就将这个远程分支作为FETCH_HEAD

一个用例: 将本地仓库回滚至远程仓库的上一个提交版本

1
git reset FETCH_HEAD^

另外, 如果要指定远程仓库的分支, 采用源名称/远程分支名的组合方式, 例如origin/master

一个用例: 将远程仓库的主干分支合并到当前分支

1
git merge origin/master

另一个用例: 以远程仓库的主干分支为基础创建新的分支

1
git checkout -b newBranch origin/master

3.4 pull

pull从远程库拉取更新, 并与本地库合并

  • 简而言之: pull = fetch + merge
  • 更具体一些: git pull = git fetch + git merge FETCH_HEAD

git pull使用给定的参数运行git fetch, 并调用git merge把FETCH_HEAD指向的版本与本地进行合并

使用方法

1
git pull <源名称> <远程分支>[:<本地分支>]

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

可以建立远程和本地、分支之间的追踪关系

1
git branch --set-upstream <本地分支> <源名称>/<远程分支>

例如:

1
git branch --set-upstream master origin/master

如果当前分支的远程追踪有多个主机, 那么源名称还是必需的

1
git pull [源名称]

如果当前分支的远程追踪只有一个, 就可以直接省略后面的所有部分

1
git pull

3.5 push

push与pull相对, 将本地仓库的分支推送到远程仓库的分支

push的用法与pull相似, 比较完整的push形式:

1
git push <源名称> <本地分支>:<远程分支>

将本地分支push到远程仓库的同名分支

1
git push <源名称> <本地分支>

如果省略本地分支, 则会清空远程分支(等同于使用空的分支覆盖远程分支)

1
2
3
git push <源名称> :<远程分支>  #不推荐
# 等同于:
git push <源名称> --delete <远程分支>

如果当前分支和远程分支有唯一的追踪关系, 就可以使用简化命令

1
git push

如果有多个源, 则需要指定源名称; 或者采取以下的指定方法, 将某个源设置为默认, 在设置后就可以直接使用简化后的命令

1
git push -u [源名称]

以上的git-push命令总结一下:

  • push需要的基本信息有: 源名称本地分支远程分支
  • 如果有唯一的追踪关系, 则不用任何指定
  • 如果有唯一的源/设定了默认源, 则不用指定源名称
  • 如果本地分支为当前分支, 则无需指定本地分支
  • 如果远程分支名本地分支名相同, 则无需指定远程分支

注意

  • git2.0及以上的版本中, 不带任何参数的git push采取simple的推送方式, 这种方式只推送当前分支
  • 而在更早的版本中, 它采用matching的推送方式, 会推送所有有对应远程分支的本地分支

可以通过以下命令, 修改推送方式:

1
2
git config --global push.default matching
git config --global push.default simple

除了simplematching, 还有一种更强硬的方式all, 这种方式会将本地的所有分支推送到远程主机

1
git push --all [源名称]

如果远程源的版本比本地更新, 就会出现推送失败的情况; 这种情况通常先使用git-pull更新本地仓库, 如果要进行强制推送, 可以使用force选项(不推荐)

1
git push --force ...

3.6 使用建议

不同的场景, 各种命令都可以进行不同程度的简化, 但在真正熟悉它们之前, 我建议按照最完整、也是最稳妥的格式使用这些命令; 在需要进阶之后, 再去简化命令

  • 一方面, 它们的使用频度不大, 多打一些参数代价极小
  • 另一方面, 完整的格式和参数, 可以帮助我们更好地理解和感知命令
  • 加之, 减少记忆量, 对入门友好
  • 此外, 明确的指定能够预防错误和混乱, 无论是git还是我们的大脑

下面是建议的使用方式:

1
2
3
4
5
6
7
8
9
10
11
# 拉取全部分支的更新
git fetch <源名称>

# 拉取特定分支的更新
git fetch <源名称> <远程分支名>

# 将指定源的指定远程分支, 拉取并合并到指定的本地分支
git pull <源名称> <远程分支>:<本地分支>

# 将指定的本地分支, 推送到指定源的指定远程分支
git push <源名称> <本地分支>:<远程分支>
This post is licensed under CC BY 4.0 by the author.

Git基本使用方法(二):本地多分支篇

CentOS7 更新git