使用git时有时需要一些技巧

我想分享一些解决一些问题的方法,这些问题有时在使用git时会出现,但并不是“直接显而易见的”。


起初我想积累更多这样的食谱,但是一切都有时间。 我认为,如果有任何好处,那么有可能一点一点地...


所以...


以最小的痛苦合并旧树枝


前言。 有一个主分支( master ),它主动提交新功能和修复; 有一个并行的feature分支,开发人员在其中航行了一段时间,来到了自己的必杀技,然后突然发现他们没有和大师一起摔倒一个月了,并且“额头”(头对头)的合并已经是微不足道的了。


(是的,这并不是一个理想的世界,在这个世界上,一切都正确,没有犯罪,孩子们总是听话,甚至严格地和妈妈一起过马路,仔细地环顾四周。)


目的:生气。 同时,这是一个“纯”合并,没有功能。 即 因此,在分支图中的公共存储库中,两个线程通过消息“将分支“主”合并到功能部件”在同一点连接。 整个“这个”头痛问题涉及到要花费多少时间和精力,解决了多少冲突以及留下了多少不必要的头发。


剧情。 事实上,您可以在git中使用密钥--amend编辑最后一次提交,这--amend众所周知。 诀窍是,该“最后一次提交”可以位于任何位置,并且可以包含任何内容。 例如,可能不仅是他们忘记纠正错字的“最后一次提交给线性分支”,而且还可能是来自普通或“章鱼”合并的合并提交。 --amend只会滚动提议的更改,并将更改的提交“嵌入”到树中,就好像它是诚实合并和解决冲突的结果而实际上出现了一样。 从本质上讲, git mergegit commit --amend允许git commit --amend将“放样地点”(“树中的此提交将在此处”)和提交本身的内容完全分开。


具有清晰历史记录的复杂合并提交的基本思想很简单:首先,我们通过创建一个干净的合并提交(无论内容如何)来“占据一席之地”,然后使用--amend重写它,使内容“正确”。


  1. “给个地方。” 通过设置不会询问有关冲突解决的不必要问题的合并策略,这很容易做到。


     git checkout feature git merge master -s ours 

  2. 哦是的 合并之前必须从功能部件头创建“备份”分支。 毕竟,什么都没有真正合并。但是让它成为第二段而不是第0段。 通常,我们切换到非合并功能,然后诚实地合并。 尽管有任何“肮脏的骇客”,但还是有可能。 我个人的方法是从上一次合并之时开始查看master分支并评估可能的问题提交(例如:在一处更正错字-不是问题。大量(许多文件)它们将实体重命名为一个问题-等等)。 从问题提交中,我们创建新分支(我天生地做了-master1,master2,master3等)。 然后我们逐个分支合并,从旧到新并纠正冲突(这种方法通常不言而喻)。 建议其他方法(我不是魔术师;我只是在学习;我将很乐意提出建设性意见!)。 最终,花了(也许)几个小时进行纯常规操作(可以委托给初级用户,因为这种方法根本没有复杂的冲突),我们得到了代码的最终状态:向导的所有创新/修复都已成功移植到功能分支,所有相关测试走上了这段代码,等等。 必须提交成功的代码。


  3. 重写“成功故事”。 在提交时,“一切都已完成”,运行以下命令:



 git tag mp git checkout mp git reset feature git checkout feature git tag -d mp 

(我破译:使用(mp-合并点)标签,我们切换到分离的HEAD状态,然后从那里将其重置为分支的头,从一开始就通过欺诈性的合并提交进行了“放样”。不再需要该标签,因此将其删除)。 现在我们处于原始的“纯”合并提交; 同时在工作副本中,我们拥有“正确”文件,可以在其中找到您需要的所有内容。 现在,您需要将所有更改的文件添加到索引中,尤其要仔细观察未暂存的文件(在主分支中将出现所有新文件)。 我们也从那里添加我们需要的一切。


最后,当一切准备就绪时,我们将正确的提交输入保留位置:


 git commit --amend 

万岁! 一切顺利! 您可以随便将一个分支推送到公共存储库中,没人会知道您实际上花了半天的时间进行此合并。


Upd:更简洁的方式


该出版物发表三个月后, capslocky发表了文章“ 如何以及为什么在git中偷树


根据她的动机,可以以较短的方式实现完全相同的目标,而无需使用辅助机制:无需“发布空间”,无需在重置后考虑未暂存的文件并进行修改; 您可以一步创建带有所需内容的直接合并提交。


我们立即开始使用任何可用方法进行合并(如上文第2段所述)。 漫不经心的中间故事和黑客仍然无关紧要。 然后,而不是权利要求3,通过合并提交的替换,我们进行了人工合并 ,如本文所述:


 git tag mp git checkout feature git merge --ff $(git commit-tree mp^{tree} -m "merged branch 'master' into 'feature'" -p feature -p master) git tag -d mp 

这里的所有魔术都是通过第三个命令(git commit-tree)一步完成的。


选择文件的一部分,保留历史记录


序言:对文件进行了编码编码,最后对其进行了编码,以至于即使是Visual Studio也开始放慢速度,进行消化(更不用说JetBrains)了。 (是的,我们回到了“不完美”的世界。一如既往)。


精明的大脑思考,思考并挑选出可以渲染到单独文件中的几个实体。 但是! 如果只是使用它,请复制并粘贴文件的一部分,然后将其粘贴到另一个文件中-从git的角度来看,这将是一个全新的文件。 如有任何问题,历史记录搜索将明确指示“该残疾人士在哪里?”是谁共享了该文件。 并且可能有必要根本不寻找“压制”的原始资源,而纯粹是建设性地寻找该源的原因。 它修复了什么错误(或未修复任何错误)。 我希望该文件是新文件,但同时仍保留整个更改历史记录!


剧情。 借助一些令人讨厌的边缘效果,可以完成此操作。 为了明确file2.txt ,有一个file.txt文件,我要从其中突出显示file2.txt的一部分。 (并且仍然保留故事,是的)。 运行以下代码段:


 f=file.txt; f1=file1.txt; f2=file2.txt cp $f $f2 git add $f2 git mv $f $f1 git commit -m"split $f step 1, converted to $f1 and $f2" 

结果,我们得到文件file1.txtfile2.txt 。 它们都有完全相同的故事(真实的;就像原始文件一样)。 是的,原始的file.txt必须重命名; 这就是“有点烦人”的边缘效果。 不幸的是,我找不到保存故事的方法,但是为了不重命名源文件(如果有人可以告诉我!)。 但是,git可以忍受一切。 现在没有人愿意在单独的提交中重命名文件:


 git mv $f1 $f git commit -m"split finish, rename $f1 to $f" 

现在为file2.txt镀金面将显示与原始文件相同的行历史记录。 最主要的是不要将这两个提交合并在一起(否则所有的魔力都会消失;我尝试过!)。 但是与此同时,没有人在分离过程中直接去编辑文件。 以后不必使用单独的提交来执行此操作。 是的,您可以一次选择多个文件!


配方的关键点是:在同一提交中将源文件重命名为另一个文件,在该文件中将其复制(并可能被编辑)为副本(副本)。 并在将来保留此提交(使用反向重命名绝不会犯错误)。


更新: 利索夫的一对食谱


分离历史存储库部分


您正在使用初始存储库的最新版本。 任务是分离一个文件夹。 (我看到了几个文件夹的选项,但是将所有内容首先折叠成一个文件夹,或者重复以下几次更加容易,也更容易理解。)


重要! 所有移动都应使用git mv ,否则git可能会丢失历史记录。


我们执行:


 git filter-branch --prune-empty --subdirectory-filter "{directory}" [branch] 

{directory}是要分离的文件夹。 结果,我们获得该文件夹以及仅提交到该文件夹​​的完整历史记录,也就是说,在每次提交中,仅显示该文件夹中的文件。 自然,部分提交将为空,--prune-empty删除它们。
现在更改原点:


 git remote set-url origin {another_repository_url}` git checkout move_from_Repo_1 

如果第二个存储库是干净的,则可以直接进入主库。 好吧,推:


 git push -u move_from_Repo_1 

整个代码段(方便复制粘贴):


 directory="directory_to_extract"; newurl="another_repository_url" git filter-branch --prune-empty --subdirectory-filter "$directory" git remote set-url origin "$newurl" git checkout move_from_Repo_1 git push -u move_from_Repo_1 

合并两个仓库


假设您执行了2次以上的操作,并且得到了早午餐move_from_Repo_1move_from_Repo_2 ,并且在每种情况下,您都使用git mv将文件传输到合并后的位置。 现在仍然可以控制:


 br1="move_from_Repo_1"; br2="move_from_Repo_2" git checkout master git merge origin/$br1 --allow-unrelated-histories git merge origin/$br2 --allow-unrelated-histories git push 

整个技巧都在--allow-unrelated-histories中。 结果,我们得到了一个存储库,其中包含所有更改的完整历史记录。

Source: https://habr.com/ru/post/zh-CN424045/


All Articles