Git for Perforce用户


86

我已经使用Perforce多年了。我想将git用作个人代码,但是我所见过的所有git教程都假定您是完整的源代码管理n00b(这使它们变得非常繁琐),或者您习惯了svn(我不是)。

我了解p4,我也了解分布式源代码控制系统背后的思想(因此,我不需要推销)。我想要的是从p4命令到等效git命令的转换表,以及没有p4等效命令的“不能没有”命令。

由于我怀疑每个p4用户都使用p4的不同子集,因此我经常希望在p4中执行一些我希望能够在git中执行的操作,这些操作在我看过的文档中并不能立即看出来:

  1. 在单个客户端中创建多个挂起的更改列表。(p4 change
  2. 编辑待处理的更改列表。(也p4 change
  3. 查看我所有待处理变更列表的列表(p4 changes -s pending
  4. 我的客户端(p4 opened)或挂起的变更列表(p4 describe)中所有已更改文件的列表
  5. 查看待处理变更列表的差异(为此,我使用了一个包装脚本,该脚本使用p4 diffp4 describe
  6. 对于给定的文件,请查看哪些已提交的更改列表影响了哪些行(p4 annotate
  7. 对于给定的文件,请参阅影响文件的变更列表的描述列表(p4 log
  8. 提交待处理的更改列表(p4 submit -c
  9. 中止待处理的变更清单(p4 revert

其中许多围绕“变更列表”。“更改列表”是p4术语。git等效项是什么?

听起来分支可能是git用户用来代替p4调用变更列表的东西。有点混乱,因为p4也有一个叫做分支的东西,尽管它们似乎只是模糊的相关概念。(尽管我一直认为p4的分支概念很奇怪,但又与经典的RCS分支概念有所不同。)

无论如何...我不确定如何用git的分支来完成我通常在p4更改列表中所做的工作。在p4中,我可以执行以下操作:

$ p4 edit a.txt
$ p4 change a.txt
Change 12345 created.

此时,我有一个包含a.txt的changlist。我可以编辑说明并继续工作而无需提交更改列表。另外,如果事实证明我需要对其他文件进行一些更改,例如在代码的其他层中进行错误修复,则可以在同一客户端中执行此操作:

$ p4 edit z.txt
$ p4 change z.txt
Change 12346 created.

现在,我在同一客户端中有两个单独的变更列表。我可以同时处理这些内容,而无需执行任何操作即可在它们之间“切换”。提交时间到了,我可以分别提交:

$ p4 submit -c 12346  # this will submit the changes to z.txt
$ p4 submit -c 12345  # this will submit the changes to a.txt

我不知道如何在git中复制它。根据我的实验,似乎没有git add与当前分支相关联。据我所知,无论何时我进入哪个分支,什么git commit时候提交我所需要的所有文件git add

$ git init
Initialized empty Git repository in /home/laurence/git-playground/.git/
$ ls
a.txt  w.txt  z.txt
$ git add -A .
$ git commit
 Initial commit.
 3 files changed, 3 insertions(+), 0 deletions(-)
 create mode 100644 a.txt
 create mode 100644 w.txt
 create mode 100644 z.txt
$ vi a.txt z.txt 
2 files to edit
$ git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   a.txt
#   modified:   z.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
$ git branch aardvark
$ git checkout aardvark
M   a.txt
M   z.txt
Switched to branch 'aardvark'
$ git add a.txt 
$ git checkout master
M   a.txt
M   z.txt
Switched to branch 'master'
$ git branch zebra
$ git checkout zebra
M   a.txt
M   z.txt
Switched to branch 'zebra'
$ git add z.txt 
$ git status
# On branch zebra
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   a.txt
#   modified:   z.txt
#
$ git checkout aardvark
M   a.txt
M   z.txt
Switched to branch 'aardvark'
$ git status
# On branch aardvark
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   a.txt
#   modified:   z.txt

在此示例中,土豚和斑马分支似乎包含完全相同的一组更改,并且基于其输出,git status似乎在其中任一位置进行提交都将具有相同的效果。难道我做错了什么?


2
假设免费的5个客户端就足够了,那么您可以将perforce用于您的个人代码。
Logan Capaldo

3
这就是我一直在做的事情,但是我想切换到开放源代码并且也被开放源代码项目使用的东西。我一直在考虑git和Mercurial。我一直倾向于git,因为它似乎有更多的动力。
劳伦斯·贡萨尔维斯

1
您最好从头开始学习Git。Git规定的工作流程与Perforce规定的工作流程完全不同。翻译后的工作流程将很尴尬,并且尝试使功能等同会混淆您的理解。幸运的是,Git社区为初学者提供了大量文档,例如。git-scm.com/book
上校恐慌

1
@ColonelPanic我可以理解您的观点,但是此类文档的问题在于,它浪费了时间来解释每个Perforce用户已经知道的基本知识。阅读此类文档就像尝试阅读另一种编程语言的教程一样令人烦恼,该教程花了一章来解释什么是变量。
劳伦斯·贡萨尔维斯

@ColonelPanic也就是说,我确实读过其他一些git文档,包括实际上非常有用的Git From Bottom UpGit for Computer Scientists。我已经使用Git几年了(请注意最初问这个问题的时间),我觉得学习git的主要问题不是缺乏文档,而是术语不佳,内部不一致,重载命令严重,以及某些未准备好未真正使用的零件。我希望有人能清理所有残留物,但这会让那些已经习惯了的人烦恼。
Laurence Gonsalves 2013年

Answers:


72

我没有太多使用perforce,所以这可能不是1:1翻译。然后,像git和mercurial这样的分布式源代码控制系统无论如何都具有不同的工作流程,因此实际上并没有(也不应该)进行1:1转换。无论如何,这里是这样:

  • 创建多个挂起的更改列表->使用分支。在git分支中,轻便而快速,创建时间不到一秒钟,并且合并时间通常不到两秒钟。不要害怕经常分支和变基。

    git branch new-branch-name
    git checkout new-branch-name
    

    或全部完成:

    git checkout -b new-branch-name
    
  • 查看所有待处理变更列表的列表->由于多个待处理变更列表的等效项是多个分支,因此只需查看分支即可:

    git branch
    

    如果还要查看远程分支:

    git branch -a
    

    在成功合并后立即删除分支是一种很好的做法,因此您不必跟踪哪个分支待合并以及哪个分支已经合并。

  • 列出所有更改的文件->对于特定分支中的单个挂起的“更改列表”,git具有索引或缓存的概念。为了提交更改,您必须首先将文件添加到该索引。这使您可以手动选择代表单个更改的文件组或忽略不相关的文件。要查看已添加文件的状态或不向该索引添加文件,请执行以下操作:

    git status
    
  • 查看待处理变更列表的差异->这有两部分。首先看一下工作目录和索引之间的区别:

    git diff
    

    但是,如果您想知道现在输入的内容与上一次提交之间的区别,那么您实际上是在要求工作目录+索引与HEAD之间的区别:

    git diff HEAD
    
  • 对于给定的文件,查看哪些提交的更改列表影响了哪些行->这很容易:

    git blame filename
    

    甚至更好,如果您处于窗口环境中:

    git gui blame filename
    

    Git gui解析文件所需的时间更长(它是用tcl而不是C编写的),但是它具有许多简洁的功能,包括通过单击提交ID来“穿越时空”回到过去的功能。我只希望他们能够实现“时空旅行”功能,以便将来找出最终解决给定错误的方法;-)

  • 对于给定的文件,请参见影响文件的变更列表的描述列表->也很容易:

    git log filename
    

    但是git log不仅仅是一个强大的工具。实际上,我的大多数个人脚本都背负git log来读取存储库。阅读手册页。

  • 提交待处理的变更列表->也很容易:

    git commit
    

请参阅我对上一个问题的回答,以了解典型的git工作流程:学习Git。需要知道我是否在正确的轨道上

如果您遵循我概述的工作流程,那么您会发现gitk之类的工具更有价值,因为它可以让您清楚地看到更改组。


附加答案:

Git非常灵活,可以通过多种方式来完成您描述的事情。要记住的事情是,始终为要使用的每个功能启动一个新分支。这意味着不会影响master分支,因此您可以随时返回它来进行错误修复。在git中工作几乎应该始于:

git checkout -b new-feature-a

现在,您可以编辑文件a.txt。要同时使用其他功能,请执行以下操作:

git checkout master
git checkout -b new-feature-z

现在您可以编辑文件z.txt。切换回a.txt:

git checkout new-feature-a

但是,等等,对new-feature-z进行了更改,git不允许您切换分支。此时,您有两种选择。首先是最简单的,将所有更改提交到当前分支:

git add .
git commit
git checkout new-feature-a

这就是我的建议。但是,如果您真的不准备提交代码,则可以暂时隐藏它:

git stash

现在,您可以切换到分支new-feature-a。要返回您正在处理的代码,只需弹出存储即可:

git checkout new-feature-z
git stash pop

完成所有操作后,将所有更改合并回master:

git merge --no-ff new-feature-a
git merge --no-ff new-feature-z

因为合并是如此之快和容易(因为冲突如此罕见且解决冲突,所以合并很容易,所以很容易),所以我们在git中使用分支来进行所有操作。

这是另一个在git中常见使用分支的示例,您在其他源代码控制工具中可能看不到(可能是Mercurial除外):

是否需要不断更改配置文件以反映您的开发环境?然后使用一个分支:

git checkout -b dev-config

现在,在您喜欢的编辑器中编辑配置文件,然后提交更改:

git add .
git commit

现在,每个新分支都可以从dev-config分支而不是master分支开始:

git checkout dev-config
git checkout -b new-feature-branch

完成后,使用交互式rebase从new-feature-branch中删除dev-config中的编辑:

git rebase -i master

删除不需要的提交,然后保存。现在,您有了一个干净的分支,无需进行自定义配置编辑。合并回母版的时间:

git checkout master
git merge --no-ff new-feature-branch
# because master have changed, it's a good idea to rebase dev-config:
git checkout dev-config
git rebase master

应当注意的是,git rebase -i当所有更改都发生在同一文件中时,甚至可以删除编辑内容。Git会记住更改,而不是文件内容*。

*注意:实际上,从技术上讲,这并不完全正确,但作为用户,这就是真实的感觉


更多其他答案:

因此,从您的评论看来,您希望同时存在两个分支,以便可以测试组合代码的工作方式。好吧,这是说明分支机构的功能和灵活性的好方法。

首先,谈谈廉价分支和可修改历史记录对工作流程的影响。当我使用CVS和SVN时,我总是有点不愿意提交。那是因为提交不稳定的代码将不可避免地使其他人的工作代码瘫痪。但是有了git我就失去了恐惧。那是因为在git中,其他人直到我将它们合并到master之前都不会得到我的更改。因此,现在我发现自己每写5行就提交一次代码。您不需要完美的先见之明就可以实施。您只需要改变心态:提交到分支==添加到变更集,合并到主==提交更改集。

因此,回到示例。这就是我的做法。假设您有一个分支,new-feature-z并且想用进行测试new-feature-a。我只是创建一个新分支来对其进行测试:

# assume we are currently in branch new-feature-z
# branch off this branch for testing
git checkout -b feature-z-and-feature-a
# now temporarily merge new-feature-a
git merge --no-ff new-feature-a

现在可以测试了。如果您需要修改某些功能以使功能-z与功能-a配合使用,则可以这样做。如果是这样,您可以将更改合并回相关分支。用于git rebase -i从合并中删除无关的更改。

或者,您也可以使用git rebase临时更改new-feature-z的基础,使其指向new-feature-a:

# assume we are currently in branch new-feature-z
git rebase new-feature-a

现在,修改了分支历史记录,因此new-feature-z将基于new-feature-a而不是master。现在可以测试了。在该分支中提交的所有更改都将属于分支new-feature-z。如果您需要修改新功能-只需切换回它并重新设置基数即可获得新的更改:

git checkout new-feature-a
# edit code, add, commit etc..
git checkout new-feature-z
git rebase new-feature-a
# now new-feature-z will contain new changes from new-feature-a

完成后,只需将其重新设置为master即可从new-feature-a中删除更改:

# assume we are currently in branch new-feature-z
git rebase master

不要害怕开始新的分支。不要害怕开始一个一次性的分支。不要害怕扔掉树枝。而且由于merge == submit和commit == add-to-changeset,所以不必害怕经常提交。请记住,提交是开发人员的最终撤消工具。

哦,另一件事,在git中,删除的分支仍然存在于您的存储库中。因此,如果您不小心删除了某些内容,但后来发现它很有用,那么您始终可以通过搜索历史记录来找回它。因此,不要害怕丢掉树枝。


1
每个分支都有自己的“索引”,还是分支之间共享一个索引?我的实验似乎建议使用后者,但您说“特定分支git具有索引或缓存的概念”,建议使用后者。
劳伦斯·贡萨尔维斯

4
这是一个不同的工具。您需要不同的工作流程,以及不同的心态和习惯。没有git不符合您的描述方式。它是分支,仅此而已。但是有非常强大的分支操作工具。我建议您将自己视为对源代码控制一无所知的新手,并阅读基础教程。考虑一下您当前的思维方式“坏习惯”,您必须在git-land中对其进行重新教育。抱歉..
slebetman 2010年

1
那么,当您有两个要同时处理的东西(并且在同一位置,以便可以相互测试未提交的更改)时,您打算在git中做什么,但是又想分别提交它们呢?另外,如何在执行提交之前编辑提交消息?我可以接受工作流程是不同的,但是您还没有说什么git等效工作流程。说这些是坏习惯,听起来很像试图将错误作为一种功能。
劳伦斯·贡萨尔维斯

2
阅读我的最新答案。当您考虑合并分支时,您仍在考虑未提交的更改。
slebetman 2010年

2
非常广泛的答案。在什么时候应该说清楚?
Tim Clemons,2010年

1

我没有足够的p4经验来制作实际的备忘单,但是至少有一些相似之处可以依靠。p4“变更集”是git“提交”。

对本地工作空间的更改将通过添加到“索引”中git add,随后使用来落实该索引git commit。因此,出于所有意图和目的,索引就是您的待处理变更列表。

您可以使用git diff和来查看更改git status,其中git diff通常显示工作空间和索引git diff --cached之间的更改,但是显示索引和存储库之间的更改(=挂起的更改列表)。

有关更多详细信息,我建议http://progit.org/book/。由于您一般都知道版本控制,因此您可能可以浏览其中的很多内容并提取特定于git的信息...


1
我不同意“ p4变更集是git commit”;它们是最接近的等效项,是的,但是p4变更集更接近一组git commit。p4变更集表示功能,而git分支表示功能。
RJFalconer 2012年

@RJFalconer Erm。“分支”也是Perforce中的分支,不是吗?“变更集”是一个或多个文件变更的原子集合,非常像提交?如果没有,您会说p4等同于commit?
雅各布·博格

我会说p4没有等效项,仅仅是因为原子性程度不同;在git中,我可以在给定的文件中进行一些更改,然后以不同的提交(git add -p)进行提交,从而将历史记录中的重构/清理与功能/错误修复分开。如果要在p4中执行此操作,则必须分别开发两者。即使那样,我的提交也可能是不连续的,因为其他开发人员可以在它们之间提交(除非是我的私有分支,这涉及在磁盘上经常不切实际的复制)。
RJFalconer 2012年

免责声明:我只使用了p4几个月,很可能它的某些功能可以实现我还没有碰到的功能。
RJFalconer 2012年

p4更改列表和p4分支在逻辑上与git commits和git分支相同。p4搁置相当于git stash。它们的不同之处在于实现方式(即分布式vs.客户服务器),并且这种差异导致git checkout的p4等效,涉及多个步骤和大量时间。这样的开销使得通常在磁盘上本地保留多个分支,而不是执行git checkout的等效操作。p4架子存储在服务器上,而不是本地仓库中。
Josh Heitzman '11

1

像您一样,我遭受着缺少“变更列表”概念的痛苦,该概念与git branch并不完全相同。

我将编写一个小脚本,该脚本将使用该更改列表中的文件列表创建一个更改列表文件。

另一条仅通过调用git commit -a @ change_list_contents.txt然后提交“ git commit”而仅提交特定变更列表的命令

希望能有所帮助,埃里亚斯


是的,我实际上已经考虑过做这样的事情,尽管当我问这个问题时,我是git的新手,所以我想知道“本机”解决方案。现在,我发现我最想念的是在没有实际提交的情况下处理提交消息的能力。因此,可以通过拥有一个文件来保存“挂起的提交消息”来解决,也许还可以通过写一些提交消息时自动读取该文件来解决。
劳伦斯·贡萨尔维斯

@LaurenceGonsalves,我使用的工作流程是在我专注于工作时提交不完善的提交消息(或什至只是“ WIP”),然后稍后在重新定型时进行修改。由于提交纯粹是本地的,因此在您使分支可用之前(通过将其推送到远程或类似位置),它们不需要是最终的。
RJFalconer 2012年

1

git中有一个更轻量的替代方案,它可以构成您的工作流程的一部分;使用git临时区域。

我经常只是进行更改,然后提交几次提交(例如,添加调试语句,重构,实际修复错误)。无需设置perforce更改列表,然后进行更改,然后提交,您可以进行更改,然后选择如何提交更改(可以选择使用git staging区域)。

您可以使用以下命令从命令行提交特定文件:

git commit a.txt
git commit z.txt

或先明确地暂存文件:

git add a.txt
git commit
git add z.txt
git commit

git gui将允许您从文件中选择行或块,以在暂存区中建立提交。如果您要更改一个文件中的更改以使其成为不同的提交,这将非常有用。从git迁移到perforce,这是我真正想念的一件事。

此工作流程需要牢记一点。如果您对文件进行A和B更改,请测试该文件,然后提交A,然后您尚未测试该提交(与B无关)。


git确实可以让您执行比perforce更细粒度的提交(即:行和块)。我(仍然)从perforce遗漏的是能够跟踪我当前正在处理的变更描述(又称为提交消息)的功能。例如,当我要修复错误#12345时,我将创建一个更改列表,说明我正在执行此操作(但又不提交提交)。然后,当我处理它时,我将更新更改描述以指示我已经完成了。最后,完成后,我将提交变更列表。
劳伦斯·贡萨尔维斯

在git中,粗略的等效方法是将那些微小的(通常是不起作用的)位提交给dev分支,然后在一切正常的情况下将更改合并。对我来说,这仍然感到乏味而笨拙。另外,我仍然没有真正习惯于提交甚至无法编译的代码。
劳伦斯·贡萨尔维斯

@LaurenceGonsalves很好奇,如果您在此期间习惯了。我要从Git进入Perforce,我想每15分钟提交一次,然后在准备就绪时再进行推送。
达米安

0

这不会具体回答您的问题,但是我不知道您是否知道可以从perforce网站免费下载和使用2用户,5工作区版本的perforce 。

这样,您可以根据需要在家中将perforce用于个人项目。令人烦恼的是5个工作区,这可能会有点限制,但是让perforce可供家庭使用实在令人难以置信。


2
这就是我一直在做的事情,但是我想切换到开放源代码并且也被开放源代码项目使用的东西。
劳伦斯·贡萨尔维斯

0

在相当广泛地使用了Perforce和git之后,我发现只有一种方法可以使git接近Perforce变更列表。

首先要了解的是,要正确地在git中实现此功能,而不是将其完全残破,例如,尝试将其刺入分支,将需要进行以下更改:git将需要单个分支的多个临时区域。

Perforce变更列表允许工作流程完全没有git版本。考虑以下工作流程:

Check out a branch
Modify file A and add it to changelist 1
Modify file B and add it to changelist 2

如果您尝试使用git中的分支来完成此操作,则将得到两个分支,其中一个分支对file进行了更改,另一个分支对fileA进行了更改B,但是没有地方可以看到两个文件的更改A以及B在同一时间。

我可以看到的最接近的近似值是使用git add . -p,然后使用'a''d'子命令选择或拒绝整个文件。但是,这并不完全相同,这里的差异源于两个系统在总体操作方式上的根本差异。

Git(以及颠覆,对于本次讨论并不重要)允许更改文件而无需事先告知任何人。您只需更改一个文件,然后在提交更改时让git对所有文件进行排序。Perforce要求您在允许更改之前主动检出文件,因此,更改列表必须存在。本质上,Perforce要求您在更改索引之前将文件添加到索引。因此,Perforce中必须有多个变更列表,还有git没有等效项的原因。 它根本不需要它们。


0

在Git 2.27(2020年第二季度)中,“ git p4”学习了四个新的钩子,并且还--no-verify选择了绕过它们的选项(和现有的p4-pre-submit钩子)。

请参阅Ben Keene()的提交1ec4a0a提交38ecf75提交cd1e0dc(2020年2月14日)和提交4935c45提交aa8b766提交9f59ca4提交6b602a2(2020年2月11日(由Junio C Hamano合并--5f2ec21提交中,2020年4月22日)seraphire
gitster

git-p4:添加p4提交钩子

签字人:本·基恩

git命令“ commit”支持许多钩子,这些钩子支持更改commit命令的行为。

git-p4.py程序只有一个现有的钩子“ p4-pre-submit”。

该命令在过程的早期发生。

在处理流程中没有钩子可用于以编程方式修改P4更改列表文本。

将3个新的钩子添加git-p4.py到Submit选项。

新的挂钩是:

  • p4-prepare-changelist-创建变更列表文件后执行此挂钩。
    即使--prepare-p4-only设置了该选项,钩子也将被执行。
    该挂钩会忽略--no-verify与的现有行为保持一致的选项git commit

  • p4-changelist-在用户编辑变更列表后执行此挂钩。
    如果用户选择了该--prepare-p4-only选项,请不要执行此挂钩。
    该钩子将--no-verify遵循的约定来兑现git commit

  • p4-post-changelist-在P4提交过程成功完成后执行此挂钩。
    该挂钩不带任何参数,并且与--no-verify选项无关都可以执行。

它的返回值将不会被检查。

对新钩子的调用:p4-prepare-changelistp4-changelistp4-post-changelist均应在try-finally块内调用。


在Git 2.28(2020年第三季度)之前,“ --prepare-p4-only”选项应该在重放一个变更集后停止,但仍继续执行(错误吗?)

参见Ben Keene()的commit 2dfdd70(2020年5月12日(由Junio C Hamano合并--commit 7a8fec9中,2020年6月2日)seraphire
gitster

git-p4.py:修复--prepare-p4-only多次提交错误

签字人:本·基恩

git p4 submit与该--prepare-p4-only选项一起使用时,程序应准备一个p4更改列表,并通知用户有更多提交待处理,然后停止处理。

p4-changelist挂钩功能引入了一个错误,该错误使程序继续尝试同时处理所有挂起的变更列表。

成功应用提交后,函数applyCommit将返回True,并且程序应继续。
但是,当--prepare-p4-only设置了可选标志时,程序应在第一个应用程序之后停止。

--prepare-p4-only成功完成applyCommit方法后,更改run方法中的逻辑以使P4Submit检查标志。

如果有1个以上的提交正在等待提交到P4,则该方法将正确准备P4更改列表,但是仍将退出代码为1的应用程序。

当前文档没有定义退出代码应该是在这种情况下什么。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.