Git笔记(23) 不同角色的贡献

TensorFlow笔记
配合深度学习笔记专栏的理论,记录TensorFlow的学习过程
氢键H-H


1. 私有小型团队

可能会遇到的最简单的配置是有一两个开发者的私有(闭源)项目
自己和其他的开发者都有仓库的推送权限

在这个环境下,可以采用一个类似使用 Subversion 或其他集中式的系统时会使用的工作流程
依然可以得到像离线提交、非常容易地新建分支与合并分支等高级功能
但是工作流程可以是很简单的,主要的区别:
合并发生在客户端这边,而不是在提交时发生在服务器那边

第一个开发者,Jove,克隆了仓库,做了改动,然后本地提交

# Jove's Machine
$ git clone Jove@githost:simplegit.git
Initialized empty Git repository in /home/Jove/simplegit/.git/
...
$ cd simplegit/
$ vim lib/simplegit.rb
$ git commit -am 'removed invalid default value'
[master 738ee87] removed invalid default value
 1 files changed, 1 insertions(+), 1 deletions(-)

第二个开发者,Jasek,做了同样的事情,克隆仓库并提交了一个改动:

#  Jasek's Machine
$ git clone Jasek@githost:simplegit.git
Initialized empty Git repository in /home/Jasek/simplegit/.git/
...
$ cd simplegit/
$ vim TODO
$ git commit -am 'add reset task'
[master fbff5bc] add reset task
 1 files changed, 1 insertions(+), 0 deletions(-)

现在,Jasek 把他的工作推送到服务器上:

# Jasek's Machine
$ git push origin master
...
To Jasek@githost:simplegit.git
   1edee6b..fbff5bc  master -> master

Jove 也尝试推送他的改动:

# Jove's Machine
$ git push origin master
To Jove@githost:simplegit.git
 ! [rejected]        master -> master (non-fast forward)
error: failed to push some refs to 'Jove@githost:simplegit.git'

这里不允许 Jove 推送是因为在同一时间 Jasek 已经推送了
尽管 Subversion 会对编辑的不同文件在服务器上自动进行一次合并
但 Git 要求你在本地合并提交
Jove 必须抓取 Jasek 的改动并合并它们,才能被允许推送

$ git fetch origin
...
From Jove@githost:simplegit
 + 049d078...fbff5bc master     -> origin/master

在这个时候,Jove 的本地仓库看起来像这样:
在这里插入图片描述
Jove (黄色)有一个引用指向 Jasek (绿色)推送上去的改动
但是他必须将它们合并入自己的工作中之后才能被允许推送

$ git merge origin/master
Merge made by recursive.
 TODO |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

合并进行地很顺利—— Jove 的提交历史现在看起来像这样:
在这里插入图片描述
现在,Jove 可以测试代码,确保它依然正常工作
然后他可以把合并的新工作推送到服务器上:

$ git push origin master
...
To jove@githost:simplegit.git
   fbff5bc..72bbc59  master -> master

最终,Jove 的提交历史看起来像这样:
在这里插入图片描述
在此期间,Jasek 在一个特性分支上工作
创建了一个称作 issue54 的特性分支并且在那个分支上做了三次提交
此时还没有抓取 Jove 的改动,所以提交历史看起来像这样:
在这里插入图片描述
Jasek 想要与 Jove 同步,所以进行了抓取操作:

# Jasek's Machine
$ git fetch origin
...
From Jasek@githost:simplegit
   fbff5bc..72bbc59  master     -> origin/master

那会同时拉取 Jove 推送的工作,Jasek 的历史现在看起来像这样:
在这里插入图片描述
Jasek 想要知道必须合并什么进入他的工作才能推送
运行 git log 来找出:

$ git log --no-merges issue54..origin/master
commit 738ee872852dfaa9d6634e0dea7a324040193016
Author: Jove <Jove@example.com>
Date:   Fri May 29 16:01:27 2009 -0700

   removed invalid default value

issue54..origin/master 语法是一个日志过滤器
它要求 Git 只显示所有在后面分支(origin/master)但不在前面分支(issue54)的提交的列表

目前,可以从输出中看到有一个 Jove 生成的但是 Jasek 还没有合并入的提交
如果他合并 origin/master,也就是说将会修改他的本地工作的那个单个提交

现在,Jasek 可以合并他的特性工作到他的 master 分支
合并 Jove 的工作(origin/master)进入他的 master 分支
然后再次推送回服务器

首先,为了整合所有这些工作他切换回他的 master 分支。

$ git checkout master
Switched to branch 'master'
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.

他既可以先合并 origin/master 也可以先合并 issue54 ——它们都是上游,所以顺序并没有关系
不论他选择的顺序是什么最终的结果快照是完全一样的
只是历史会有一点轻微的区别

加入他选择先合并入 issue54:

$ git merge issue54
Updating fbff5bc..4af4298
Fast forward
 README           |    1 +
 lib/simplegit.rb |    6 +++++-
 2 files changed, 6 insertions(+), 1 deletions(-)

没有发生问题;如你所见它是一次简单的快进
现在 Jasek 合并入 Jove 的工作(origin/master):

$ git merge origin/master
Auto-merging lib/simplegit.rb
Merge made by recursive.
 lib/simplegit.rb |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

每一个文件都干净地合并了,Jasek 的历史看起来像这样:
在这里插入图片描述
现在 origin/master 是可以从 Jasek 的 master 分支到达的
所以他应该可以成功地推送(假设同一时间 Jove 并没有再次推送):

$ git push origin master
...
To jasek@githost:simplegit.git
   72bbc59..8059c15  master -> master

每一个开发者都提交了几次并成功地合并了其他人的工作
在这里插入图片描述
这是一个最简单的工作流程:

  • 通常在一个特性分支工作一会儿
  • 当它准备好整合时合并回自己的 master 分支
  • 当想要共享工作时,同样将其合并回自己的 master 分支
  • 如果有改动的话然后抓取并合并 origin/master,使现在的 master 分支能指向origin/master
  • 最终推送到服务器上的 master 分支

通常顺序像这样:
在这里插入图片描述


2. 私有管理团队

小组基于特性进行协作,这些团队的贡献将会由其他人整合
加粗样式
假设 Jove 与 Jasek 在一个特性上工作,同时 Jasek 与 Wallace 在第二个特性上工作
而公司使用了一种整合-管理者工作流程:
独立小组的工作只能被特定的工程师整合,主仓库的 master 分支只能被那些工程师更新
在这种情况下,所有的工作都是在基于团队的分支上完成的并且稍后会被整合者拉到一起

因为 Jasek 在两个特性上工作,并且平行地与两个不同的开发者协作
假设他已经克隆了仓库,首先决定在 featureA 上工作
他为那个特性创建了一个新分支然后在那做了一些工作:

# Jasek's Machine
$ git checkout -b featureA
Switched to a new branch 'featureA'
$ vim lib/simplegit.rb
$ git commit -am 'add limit to log function'
[featureA 3300904] add limit to log function
 1 files changed, 1 insertions(+), 1 deletions(-)

在这个时候,需要将工作共享给 Jove,所以推送了 featureA 分支的提交到服务器上

Jasek 没有 master 分支的推送权限(只有整合者有),所以为了与 Jove 协作必须推送另一个分支

$ git push -u origin featureA
...
To Jasek@githost:simplegit.git
 * [new branch]      featureA -> featureA

Jasek 向 Jove 发邮件告诉他已经推送了一些工作到 featureA 分支现在可以看一看
当他等待 Jove 的反馈时,Jasek 决定与 Wallace 开始在 featureB 上工作
为了开始工作,基于服务器的 master 分支开始了一个新分支

# Jasek's Machine
$ git fetch origin
$ git checkout -b featureB origin/master
Switched to a new branch 'featureB'

现在,Jasek 在 featureB 分支上创建了几次提交:

$ vim lib/simplegit.rb
$ git commit -am 'made the ls-tree function recursive'
[featureB e5b0fdc] made the ls-tree function recursive
 1 files changed, 1 insertions(+), 1 deletions(-)
$ vim lib/simplegit.rb
$ git commit -am 'add ls-files'
[featureB 8512791] add ls-files
 1 files changed, 5 insertions(+), 0 deletions(-)

现在,Jasek 的仓库看起来像这样:

在这里插入图片描述
准备好推送工作了
但是来自 Wallace 的邮件告知一些初始工作已经被推送到服务器上的 featureBee 上了
那么 Jasek 在能推送到服务器前首先需要将那些改动与他自己的合并
现在可以通过 git fetch 抓取 Wallace 的改动:

$ git fetch origin
...
From Jasek@githost:simplegit
 * [new branch]      featureBee -> origin/featureBee

Jasek 现在可以通过 git merge 将其合并到他做的工作中:

$ git merge origin/featureBee
Auto-merging lib/simplegit.rb
Merge made by recursive.
 lib/simplegit.rb |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

有点儿问题,需要将在 featureB 分支上合并的工作推送到服务器上的 featureBee 分支
这里可以通过 指定本地分支加上冒号(:) 加上远程分支给 git push 命令来这样做:

$ git push -u origin featureB:featureBee
...
To Jasek@githost:simplegit.git
   fba9af8..cd685d1  featureB -> featureBee

这称作一个引用规格

紧接着,Jove 发邮件给 Jasek 说他已经推送了一些改动到 featureA 分支并要求他去验证它们
Jasek 赶紧运行一个 git fetch 来拉取下那些改动:

$ git fetch origin
...
From Jasek@githost:simplegit
   3300904..aad881d  featureA   -> origin/featureA

然后,通过 git log 可以看到哪些发生了改变:

$ git log featureA..origin/featureA
commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6
Author: Jove <Jove@example.com>
Date:   Fri May 29 19:57:33 2009 -0700

    changed log output to 30 from 25

最终,合并 Jove 的工作到自己的 featureA 分支:

$ git checkout featureA
Switched to branch 'featureA'
$ git merge origin/featureA
Updating 3300904..aad881d
Fast forward
 lib/simplegit.rb |   10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)

Jasek 想要轻微调整一些东西,所以再次提交然后将其推送回服务器:

$ git commit -am 'small tweak'
[featureA 774b3ed] small tweak
 1 files changed, 1 insertions(+), 1 deletions(-)
$ git push
...
To Jasek@githost:simplegit.git
   3300904..774b3ed  featureA -> featureA

Jasek 的提交历史现在看起来像这样:
在这里插入图片描述
Jasek、Wallace 与 Jove 通知整合者在服务器上的 featureAfeatureBee 分支准备好整合到主线中了
在整合者合并这些分支到主线后,一次抓取会拿下来一个新的合并提交
历史看起来像这样:
在这里插入图片描述
许多团队切换到 Git 是因为这一允许多个团队并行工作、并在之后合并不同工作的能力
团队中更小一些的子小组可以通过远程分支协作
而不必影响或妨碍整个团队的能力
在这儿看到的工作流程顺序类似这样:
在这里插入图片描述


3. 派生的公开项目

向公开项目做贡献有一点儿不同
因为没有权限直接更新项目的分支,必须用其他办法将工作给维护者

这里描述在支持简单派生的 Git 托管上使用派生来做贡献
许多托管站点支持这个功能,包括 GitHub、BitBucket、Google Code等

首先,可能想要克隆主仓库
为计划贡献的补丁或补丁序列创建一个特性分支,然后在那儿做工作
顺序看起来基本像这样:

$ git clone (url)
$ cd project
$ git checkout -b featureA
# (work)
$ git commit
# (work)
$ git commit

可能会想要使用变基 rebase -i 来将工作压缩成一个单独的提交
或者重排提交中的工作使补丁更容易被维护者审核

当分支工作完成后准备将其贡献回维护者
去原始项目中然后点击 “Fork” 按钮,创建一份自己的可写的项目 派生仓库
在这里插入图片描述
然后需要添加这个新仓库 URL 为第二个远程仓库,在本例中称作 myfork

$ git remote add myfork (url)

然后需要推送工作到上面

$ git push -u myfork featureA

相对于合并到主分支再推送上去,推送正在工作的特性分支到仓库上更简单
原因是工作如果不被接受或者是被拣选的,就不必回退自己的 master 分支
如果维护者合并、变基或拣选你的工作
不管怎样你最终会通过拉取他们的仓库找回来你的工作

当工作已经被推送到派生后,需要通知维护者
这通常被称作一个拉取请求(pull request),既可以通过网站生成它
在这里插入图片描述
GitHub 有它自己的 Pull Request 机制

也可以运行 git request-pull 命令然后手动地将输出发送电子邮件给项目的维护者

request-pull 命令接受特性分支拉入的基础分支,以及它们拉入的 Git 仓库 URL
输出请求拉入的所有修改的总结

例如,Jasek 想要发送给 Jove 一个拉取请求
他已经在刚刚推送的分支上做了两次提交,可以运行这个:

$ git request-pull origin/master myfork
The following changes since commit 1edee6b1d61823a2de3b09c160d7080b8d1b3a40:
  Jove Smith (1):
        added a new function

are available in the git repository at:

  git://githost/simplegit.git featureA

Jasek (2):
      add limit to log function
      change log output to 30 from 25

 lib/simplegit.rb |   10 +++++++++-
 1 files changed, 9 insertions(+), 1 deletions(-)

这个输出可以被发送给维护者
它告诉他们工作是从哪个分支开始、归纳的提交与从哪里拉入这些工作

在一个你不是维护者的项目上
通常有一个总是跟踪 origin/mastermaster 分支会很方便
在特性分支上做工作是因为如果它们被拒绝时你可以轻松地丢弃

如果同一时间主仓库移动了然后你的提交不再能干净地应用
那么使工作主题独立于特性分支也会使你变基(rebase)工作时更容易

例如,想要提供第二个特性工作到项目,不要继续在刚刚推送的特性分支上工作
从主仓库的 master 分支重新开始:

$ git checkout -b featureB origin/master
# (work)
$ git commit
$ git push myfork featureB
# (email maintainer)
$ git fetch origin

现在,每一个特性都保存在一个贮藏库中,类似于补丁队列
可以重写、变基与修改而不会让特性互相干涉或互相依赖,像这样:
在这里插入图片描述
假设项目维护者已经拉取了一串其他补丁
然后尝试拉取你的第一个分支,但是没有干净地合并

在这种情况下,可以尝试 变基那个分支到 origin/master 的顶部,为维护者解决冲突
然后重新提交改动:

$ git checkout featureA
$ git rebase origin/master
$ git push -f myfork featureA

这样会重写你的历史,现在看起来像是 featureA 工作之后的提交历史
在这里插入图片描述
因为将分支变基了,所以必须为推送命令指定 -f 选项
这样才能将服务器上有一个不是它的后代的提交的 featureA 分支替换掉
一个替代的选项是推送这个新工作到服务器上的一个不同分支(可能称作 featureAv2

让我们看一个更有可能的情况:
维护者看到了你的第二个分支 featureB 上的工作并且很喜欢其中的概念
但是想要你修改一下实现的细节
你也可以利用这次机会将工作基于项目现在的 master 分支
你从现在的 origin/master 分支开始一个新分支
在那儿压缩 featureB 的改动,解决任何冲突,改变实现,然后推送它为一个新分支

$ git checkout -b featureBv2 origin/master
$ git merge --squash featureB
# (change implementation)
$ git commit
$ git push myfork featureBv2

--squash 选项接受被合并的分支上的所有工作,并将其压缩至一个变更集
使仓库变成一个真正的合并发生的状态,而不会真的生成一个合并提交

这意味着你的未来的提交将会只有一个父提交,并允许你引入另一个分支的所有改动
然后在记录一个新提交前做更多的改动

同样 --no-commit 选项在默认合并过程中可以用来延迟生成合并提交

现在可以给维护者发送一条消息,表示已经做了要求的修改
然后他们可以在 featureBv2 分支上找到那些改动

在这里插入图片描述


4. 通过邮件的公开项目

许多项目建立了接受补丁的流程
需要检查每一个项目的特定规则,因为它们之间有区别
因为有几个历史悠久的、大型的项目会通过一个开发者的邮件列表接受补丁

现在将会通过一个例子来演示
工作流程与之前的用例是类似的:为工作的每一个补丁序列创建特性分支
区别是如何提交它们到项目中

生成每一个提交序列的电子邮件版本然后邮寄它们到开发者邮件列表
而不是派生项目然后推送到你自己的可写版本

$ git checkout -b topicA
# (work)
$ git commit
# (work)
$ git commit

现在有两个提交要发送到邮件列表
使用 git format-patch 来生成可以邮寄到列表的 mbox 格式的文件
它将每一个提交转换为一封电子邮件,提交信息的第一行作为主题,剩余信息与提交引入的补丁作为正文

有一个好处是生成的一封电子邮件应用的提交正确地保留了所有的提交信息

$ git format-patch -M origin/master
0001-add-limit-to-log-function.patch
0002-changed-log-output-to-30-from-25.patch

打印出它创建的补丁文件名字, -M 开关告诉 Git 查找重命名

文件最后看起来像这样:

$ cat 0001-add-limit-to-log-function.patch
From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica <jessica@example.com>
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] add limit to log function

Limit log functionality to the first 20

---
 lib/simplegit.rb |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index 76f47bc..f9815f1 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -14,7 +14,7 @@ class SimpleGit
   end

   def log(treeish = 'master')
-    command("git log #{treeish}")
+    command("git log -n 20 #{treeish}")
   end

   def ls_tree(treeish = 'master')
--
2.1.0

也可以编辑这些补丁文件为邮件列表添加更多不想要在提交信息中显示出来的信息
如果在 --- 行与补丁开头(diff --git 行)之间添加文本,那么开发者就可以阅读它
但是应用补丁时会排除它

为了将其邮寄到邮件列表
既可以将文件粘贴进电子邮件客户端,也可以通过命令行程序发送它
但粘贴文本经常会发生格式化问题

幸运的是,Git 提供了一个工具帮助你通过 IMAP 发送正确格式化的补丁
现在将会演示如何通过 Gmail 发送一个补丁

可以在之前提到在 Git 源代码 中的 Documentation/SubmittingPatches 文件 的最下面了解一系列邮件程序的详细指令

首先,需要在 ~/.gitconfig 文件中设置 imap 区块
可以通过一系列的 git config 命令来分别设置每一个值,或者手动添加它们
不管怎样最后配置文件应该看起来像这样:

[imap]
  folder = "[Gmail]/Drafts"
  host = imaps://imap.gmail.com
  user = user@gmail.com
  pass = p4ssw0rd
  port = 993
  sslverify = false

如果 IMAP 服务器不使用 SSL,最后两行没有必要,且host 会是 imap:// 而不是 imaps://
当那些设置完成后,可以使用 git imap-send 将补丁序列放在特定 IMAP 服务器的 Drafts 文件夹中:

$ cat *.patch |git imap-send
Resolving imap.gmail.com... ok
Connecting to [74.125.142.109]:993... ok
Logging in...
sending 2 messages
100% (2/2) done

在这个时候,应该能够到 Drafts 文件夹中
修改收件人字段为想要发送补丁的邮件列表
可能需要抄送给维护者或负责那个部分的人,然后发送

也可以通过一个 SMTP 服务器发送补丁
同之前一样,可以通过一系列的 git config 命令来分别设置选项
或者手动地将它们添加到 ~/.gitconfig 文件的 sendmail 区块:

[sendemail]
  smtpencryption = tls
  smtpserver = smtp.gmail.com
  smtpuser = user@gmail.com
  smtpserverport = 587

当这完成后,可以使用 git send-email 发送补丁:

$ git send-email *.patch
0001-added-limit-to-log-function.patch
0002-changed-log-output-to-30-from-25.patch
Who should the emails appear to be from? [Jasek <Jasek@example.com>]
Emails will be sent from: Jasek <Jasek@example.com>
Who should the emails be sent to? Jasek@example.com
Message-ID to be used as In-Reply-To for the first email? y

然后,对于正在发送的每一个补丁,Git 会吐出这样的一串日志信息:

(mbox) Adding cc: Jasek<Jasek@example.com> from
  \line 'From: Jasek <Jasek@example.com>'
OK. Log says:
Sendmail: /usr/sbin/sendmail -i Jasek@example.com
From: Jasek <Jasek@example.com>
To: Jasek@example.com
Subject: [PATCH 1/2] added limit to log function
Date: Sat, 30 May 2009 13:29:15 -0700
Message-Id: <1243715356-61726-1-git-send-email-Jasek@example.com>
X-Mailer: git-send-email 1.6.2.rc1.20.g8c5b.dirty
In-Reply-To: <y>
References: <y>

Result: OK

参考: git
以上内容,均根据git官网介绍删减、添加和修改组成


相关推荐:

Git笔记(22) 项目贡献要点
Git笔记(21) 分布式工作流程
Git笔记(20) 配置服务器
Git笔记(19) 生成SSH公钥
Git笔记(18) 搭建服务器Git


谢谢

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: Age of Ai 设计师: meimeiellie
应支付0元
点击重新获取
扫码支付

支付成功即可阅读