在Git中分支特定的行 [英] Branch specific lines in Git

查看:119
本文介绍了在Git中分支特定的行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对不起,我的问题太模糊了,但我真的不知道如何描述最好的情况。



在我的仓库中, README 文件包含一个链接,其中包含分支名称 master develop 各自的分支机构(只有那两个分支机构受到影响,我并不在乎它是如何处理不同分支机构的)。我现在想要的是,如果我将 develop 合并到 master 中,这种差异就会被保留。



到目前为止,我使用的是

  git merge --no-commit develop 

并手动修复链接。这里的问题在于, develop 具有独立的 master 历史。因此, develop 分支中的 git describe 不包含master上的最新标记。然而,我使用这个来进行版本控制,所以有必要这样做。解决方法是将 master 返回到 develop

  git merge --no-commit master 

然而,导致一个混乱的历史,因为我有很多这些空的反向合并。



我也试过这个,我在这里添加 README.rst merge = ours 。但是,这会忽略对 README 全部更改,因此我将不得不手动更新 README 文件。



我不知道是否有合适的方法。有没有办法让GitHub忽略链接(例如通过正则表达式)?或者可能有一些扩展到分支名称的命令?

解决方案

你不能(很)得到你想要的。你可能能够得到你需要的东西,取决于你需要它的地方。考虑使用涂抹过滤器在检出文件时更改文件。您需要一个相应的清理过滤器来移除污迹,以便实际提交中的内容不被污染。



有关设置涂抹和清洁过滤器,请参阅 gitattributes 文档



背景



首先,记住一个分支 name 只是一个指向(单个)提交的可移动指针。还有一些其他人也可以使用分支这个词,它是:以特定提交结束的一系列提交。有关详细信息,请参阅我们的意思是分支?



一般来说, Git所做的是添加提交。您可以查看一些分行名称:

  $ git checkout master 

,然后在您的工作树中的文件中进行一些工作,然后将其复制到临时区域也就是 index 使用 git add 来覆盖已经在索引中但与当前提交匹配的副本 - 我们将回到所有这些)

  ...编辑... 
$ git add README.rst

最后,运行 git commit ,其中:




  • 收集您的日志信息;
  • 将所有包装 现在在索引中;和
  • 使用这些(加上您的姓名和电子邮件以及时间)进行新的提交。

    当您运行 git checkout master 时,新提交的父提交是您签出的提交。当您运行 git checkout master 时,提交成为您选择的分支的提示。因此,现在 master 指向 new 提交,该提交指向 master 立即提交:

      ...< -previous-tip-of -master-master-master -b 


    $ p $你只能检查一次只支持一个分支,并且 git commit 分支上进行新的提交 - 特别是通过使 >指向Git刚刚提交的新提交。所有其他分支名称保持不变:它们仍指向其他一个或多个提交。



    使用 git merge 仍然只是做了一个新的承诺!这个新提交与 git commit 所做的其他新提交之间的区别在于,这个新提交具有两个父项:它指向以前的提交像往常一样,但它也指向您在运行 git merge 时选择的提交。在任何情况下,这个新的提交只是在你所在的分支 上:

      ... -  B  -  ...  -  o  -  C  -  M  -   - 主
    \ /
    o --...- o - D < - develop

    其中 M 是新的合并提交。这个新的合并提交包含所有文件的快照,就像其他所有提交都具有所有文件的快照一样,所以 M中的 README.rst 文件是通过在 C 中创建快照来完成的,它是 master 前一段时间 - 以及 D 中的快照,它仍然是开发。这些事情是基于自从合并基础之后 C D 在提交 B 中的快照中提交 README.rst 版本。



    利用这个:smudge和clean filters



    现在,连同这个元数据以及谁是谁做的,什么时候什么日志消息,每个 commit 只需记录运行 git commit 时索引/登台区域中的所有源文件。当您< git checkout< thing-that-specify-commit> 时,您得到的是这个快照派生出来的,但不一定是相同。



    所有这些细节都很重要! Git会将 M 中的提交提取到索引/分段区域。无论是在 M 中,这个索引/暂存区域中的内容完全匹配。但这不是你在工作树中看到的。您在工作树中看到的内容是通过运行任何Git复制到索引中,通过您定义的任何涂抹过滤器获得的。



    默认涂抹过滤器基本上是保持一切不变。您可以在此时进行结束调整。从技术上讲,这与污迹过滤器是分开的,但它是所有污迹过程的一部分。如果您使用此默认值,则工作树中的内容与您索引中的内容匹配。



    假设您定义了一个涂抹过滤器,专门应用于 README.rst 。进一步假设你编写了这个污迹过滤器来找到应该提及分支的链接, master develop 。将您的涂抹过滤器写入正确的链接替换此行。例如,也许你提交的行应该是这样的:

     等等等等等等! blah 

    并且您的涂抹过滤器显示:

    <$使用$ branch /

    $ b $替换这部分!/实际链接pre> sed -es /! b

    (使用Linux / Unix / GNU sed 命令,但是您可以根据自己的喜好编写它)。使你的污点过滤器代码找出使用哪个分支名称,例如,运行 git rev-parse --abbrev-ref HEAD



    这意味着文件 README.rst 的工作树中的内容将不再与实际提交的 匹配。 p>

    尽管如此,您需要将 git add 写入,承诺,一行内容如下:

     等等等等等等!为了自动执行此操作,请自己写一个 clean 过滤器,以便找到弄脏了一行 - 不过你选择这么做 - 并用 sed 代替它,替换这部分! 

    现在当你运行时:

      $ git添加README.rst 

    Git将通过您的干净过滤器运行工作树文件以获取版本 README.rst 写入索引/分段区域。



    合并时这也适用:merge 查看文件的已清理版本,并使用已清除的文本合并您在任一分支上所做的任何更改。具有实际链接的脏(污迹)版本仅出现在工作树中。它永远不会进入任何提交。



    这是否符合您的需求取决于读取链接的内容。如果不管读取链接,通过提取对工作树的提交并使用涂抹过滤器来完成,那么读取文件的内容将读取被污染的实际链接。如果文件以某种方式读取文件绕过所有这些并获取原始提交的内容,它将会看到已清除链接。



    如果将清理后的链接存储为版本适用于大多数用途,并且只需要指向开发而不是 master 的脏版本工作树,即使在这种情况下也是如此。


    Sorry that my question is so vague, but I don't really know how to describe the situation best.

    In my repository the README file contains a link, which has the branch name master or develop in it for the respective branches (only those two branches are affected, I don't really care how it is handled for different branches). What I want now, is that if I merge develop into master this difference is preserved.

    What I did up to now, was using

    git merge --no-commit develop
    

    And manually fix the link. The problem here is, that develop has then an independent history of master. Thus, git describe from the develop branch doesn't include the latest tag on master. I use this however for versioning, so it is essential to have this. A work around is to then merge master back into develop

    git merge --no-commit master
    

    This however causes a messy history, as I have plenty of these empty back-merges.

    I also tried this, where I add README.rst merge=ours. However, this ignores all changes to README thus I would have to manually update the README file.

    I don't really know if there is a suitable approach. Is there a way I can tell GitHub to ignore the links (e.g. via a regular expression)? Or maybe some command that expands into the branch name?

    解决方案

    You can't (quite) get what you want. You might be able to get what you need, depending on where you need it. Consider using a smudge filter to alter the file as it gets checked out. You need a corresponding clean filter to remove the smudging, so that what's in the actual commits is un-smudged.

    For more about setting up smudge and clean filters, see the gitattributes documentation.

    Background

    First, remember that a branch name is just a moveable pointer to a (single) commit. There is something else that people also refer to using the word branch, which is: a series of commits ended by a particular commit. For more about this, see What exactly do we mean by "branch"?

    In general, what Git does is add commits. You check out some branch name:

    $ git checkout master
    

    and then do some work (on files in your work-tree, which you then copy into the staging area aka index using git add to overwrite the copies that were already in the index but match the current commit—we'll come back to all this in a bit):

    ... edit ...
    $ git add README.rst
    

    Eventually, you run git commit, which:

    • collects a log message from you;
    • packages up all the files that are in the index right now; and
    • uses those (plus your name and email and the time) to make a new commit.

    The new commit's parent commit is the commit you checked out, when you ran git checkout master. The new commit becomes the one at the tip of the branch you chose, when you ran git checkout master. So the name master now points to the new commit, which points back to what was the master commit just a moment ago:

    ...  <-previous-tip-of-master  <-new-tip-of-master   <--master
    

    You can only check out one branch at a time, and git commit makes its new commit on that branch—specifically, by making that name point to the new commit Git just made. All other branch names are unchanged: they still point to some other commit(s).

    Using git merge still just makes a new commit! The difference between this new commit and other new commits made by git commit is that this new commit has two parents: it points back to the previous commit as usual, but it also points back to the commit you chose when you ran git merge. In any case this new commit is only on the branch you're on:

    ...--B--...--o--C--M   <-- master
          \           /
           o--...-o--D   <-- develop
    

    where M is the new merge commit. This new merge commit has a snapshot of all files, just like every other commit has a snapshot of all files, so the README.rst file in M is built by doing something(s) with the snapshot in C—which was the tip of master a moment ago—and something(s) with the snapshot in D, which is still the tip of develop. These "somethings" are based on how C and D have changed since the merge base commit version of README.rst that's in the snapshot in commit B.

    Making use of this: smudge and clean filters

    Now, along with this metadata about who made it, when, and with what log message, each commit simply records all the source files that were in the index / staging-area at the time you ran git commit. What you get when you git checkout <thing-that-specifies-commit> is derived from this snapshot, but is not necessarily the same as this snapshot.

    All of these details are important! Git will extract the commit in M into the index / staging-area. What's in this index / staging-area at this point exactly matches whatever is inside M. But this isn't what you see in your work-tree. What you see in your work-tree is obtained by running whatever Git copied into the index through any smudge filter you define.

    The default smudge filter is basically "leave everything unchanged". You can get end-of-line adjustments to happen at this point. Technically this is separate from smudge filters, but it's all part of this smudging process. If you use this default, what's in your work-tree matches what's in your index.

    Suppose, though, that you define a smudge filter to apply specifically to README.rst. Suppose further that you write this smudge filter to find the line with the link that is supposed to mention the branch, master or develop. Write your smudge filter to replace this line with the correct link. For instance, perhaps the line you commit should read:

    blah blah link:!replace this part! blah
    

    and your smudge filter says:

    sed -e "s/!replace this part!/actual link using $branch/"
    

    (using the Linux/Unix/GNU sed command, but you can write this however you like). Make your smudge filter code figure out what branch name to use by, e.g., running git rev-parse --abbrev-ref HEAD.

    This means that what's in the work-tree for file README.rst will no longer match what's actually committed.

    To make this work properly, though, you need to make git add write, as the thing-to-be-committed, a line that reads:

    blah blah link:!replace this part! blah
    

    To do this automatically, write yourself a clean filter that finds the smudged line—however you choose to do that—and replaces it, e.g., using sed, with the !replace this part! text.

    Now when you run:

    $ git add README.rst
    

    Git will run the work-tree file through your clean filter to get the version of README.rst to write into the index / staging-area.

    This also works when merging: the merge will "see" the cleaned version of the file, and merge any changes you make, on either branch, using the cleaned text. The "dirty" (smudged) version that has the actual link appears only in the work-tree. It never goes into any commit.

    Whether this does what you need depends on what reads the link. If whatever reads the link, does so by extracting commits to a work-tree, and uses the smudge filter, than that thing reading the file will read the dirtied-up actual link. If the thing reading the file somehow bypasses all of this and gets the raw committed content, it will see the "cleaned" link.

    If you store the cleaned link as a version suitable for most uses, and only need the dirty version that points to develop instead of master when used out of the work-tree, this will work even for that case as well.

    这篇关于在Git中分支特定的行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆