git recursive merge strategy options within .gitattributes












0















I'm struggling with forcing a specific merge strategy (-s recursive -Xours) for certain files within Git's .gitattributes file.



I have been playing with both .git/config and .gitattributes files with no success, what I managed so far does not take into account the -X option.



.gitattributes



*pom.xml merge=recursive


.git/config



[merge "recursive"]
driver = true









share|improve this question





























    0















    I'm struggling with forcing a specific merge strategy (-s recursive -Xours) for certain files within Git's .gitattributes file.



    I have been playing with both .git/config and .gitattributes files with no success, what I managed so far does not take into account the -X option.



    .gitattributes



    *pom.xml merge=recursive


    .git/config



    [merge "recursive"]
    driver = true









    share|improve this question



























      0












      0








      0








      I'm struggling with forcing a specific merge strategy (-s recursive -Xours) for certain files within Git's .gitattributes file.



      I have been playing with both .git/config and .gitattributes files with no success, what I managed so far does not take into account the -X option.



      .gitattributes



      *pom.xml merge=recursive


      .git/config



      [merge "recursive"]
      driver = true









      share|improve this question
















      I'm struggling with forcing a specific merge strategy (-s recursive -Xours) for certain files within Git's .gitattributes file.



      I have been playing with both .git/config and .gitattributes files with no success, what I managed so far does not take into account the -X option.



      .gitattributes



      *pom.xml merge=recursive


      .git/config



      [merge "recursive"]
      driver = true






      git version-control






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 22 '18 at 10:49







      codeJack

















      asked Nov 22 '18 at 10:46









      codeJackcodeJack

      85531326




      85531326
























          1 Answer
          1






          active

          oldest

          votes


















          0














          TL;DR



          Use git merge-file in your merge driver. Here, you can specify the options you want. You're asking for --ours. Be aware that this does not always work right. You might even want --union, but be aware that this does not really work right either. See Can git be made to mostly auto-merge XML order-insensitive files? and VS 2013 C# and Git: .csproj files don't merge upon pulling for issues with trying to use either method with XML files.



          Long



          First, let's define the terms the way Git uses them:





          • A strategy is the argument you pass to the -s flag to git merge: git merge -s recursive, git merge -s resolve, git merge -s ours, or git merge -s octopus. (In practice you would never use the last one explicitly—instead, it's implied if you ask Git to merge multiple heads.)



            In other words, this is one of the names ours, recursive, or resolve. In theory, you can write your own Git merge strategy as well, but that's very non-trivial (so much so that I have not heard of anyone doing it, though this might not mean that much).



          • A strategy option the argument you pass to the -X flag, such as -X ours or -X rename-threshold=<number>. I find this to be a bad name, as it's too easy to confuse with the strategy. This is especially true when speaking: did you say "strategy" and then stop speaking but mean "strategy option" and you were just interrupted, or did you say "strategy" and stop speaking and actually mean "strategy"? I like to call these extended options, which goes well with the -X flag, which then stands for "eXtended".)


          • A driver is something you define in a .gitattributes file. There are several flavors (filter, diff, and merge); here we're concerned with merge drivers. Merge drivers are invoked by a strategy; the driver could, in theory, have access to at least some of the extended-options, but in practice they don't. (Git should define %-expanders that provide them, and/or provide them in an environment variable; this would be relatively straightforward.)



          With these definitions in mind, it's easy to see that this:




          I'm struggling with forcing a specific merge strategy (-s recursive -Xours) for certain files within Git's .gitattributes file.




          is literally impossible. That's true whether you mean strategy or strategy option here: both have been chosen long before we get to the merge driver defined in the .gitattributes file.



          All is not necessarily lost, though!



          You mention specifically pom.xml, for which you want a merge driver—the thing you can define in .gitattributes—that does a text style merge but takes "ours" if there is a conflict, a la the extended-option "ours". What you have, instead, is a merge driver that will do an "ours" strategy merge: ignore both the merge base and their changes entirely, just taking "our" version of the file.



          Compare your attempt with the answer at Whats the Proper usage of .gitattributes with merge=ours: the only difference is the spelling of the driver name, where you use recursive instead of ours. The name of the driver is not important, as long as it's not one of the known magic names (text, binary, and union): Git will run your driver instead of doing its own file-level merge, when it determines that there is a file-level merge to be performed.



          The gitattributes documentation has this to say about defining a merge driver:




          The definition of a merge driver is done in the .git/config file, not in the gitattributes file, so strictly speaking this manual page is a wrong place to talk about it. However...



          To define a custom merge driver filfre, add a section to your $GIT_DIR/config file (or $HOME/.gitconfig file) like this:



          [merge "filfre"]
          name = feel-free merge driver
          driver = filfre %O %A %B %L %P
          recursive = binary



          (Side note: "recursive = binary" is not necessarily good advice, but let's press on here for now.)




          The merge.*.name variable gives the driver a human-readable name.



          The merge.*.driver variable’s value is used to construct a command to run to merge ancestor’s version (%O), current version (%A) and the other branches’ version (%B). These three tokens are replaced with the names of temporary files that hold the contents of these versions when the command line is built. Additionally, %L will be replaced with the conflict marker size (see below).




          That is, %O names a temporary file containing the merge-base version of the file, while %A and %B name temporary files containing the --ours and --theirs versions of the file. Your job, as filter-driver-writer, is to come up with a command that will perform the merge:




          The merge driver is expected to leave the result of the merge in the file named with %A by overwriting it, and exit with zero status if it managed to merge them cleanly, or non-zero if there were conflicts.




          Having performed the merge, you should overwrite the %A-named file with the result. Since the %A-named file contains the --ours contents, a driver that does nothing, but then exits, declares that the correct contents for merging %O vs %A and %B is whatever is already in %A.



          In other words, this is like the ours strategy. It is not like the merge-favoring-ours-in-case-of-conflicts, resolve-or-recursive-with--Xours strategy-plus-option. But you don't want a whole strategy at all, as that deals with entire commits; you want a single file operation. This is where git merge-file comes in. Let's finish off the rest of the description now, though:




          The merge.*.recursive variable specifies what other merge driver to use when the merge driver is called for an internal merge between common ancestors, when there are more than one. When left unspecified, the driver itself is used for both internal merge and the final merge.




          This is a bit confusing, but it applies only to the recursive strategy, and even then, only if git merge-base --all HEAD <theirs> prints out multiple hash IDs. Most of the time, you don't need anything special here at all. Using recursive = binary is equivalent to setting, for this one file, the strategy to resolve, because the binary internal merge driver just takes the ours version, which means that when -s recursive repeatedly merges merge bases, it just gets the first merge base version back each time.



          Multiple merge bases are rare, and it's not always clear what to do with them, but omitting any setting will cause the internal recursive merges to use your merge driver, which is probably fine for most cases. Your driver won't be called recursively; the recursion happens outside the calls to your driver. If you view the recursion as a tree, your driver is, in effect, called as a post-order traversal.




          The merge driver can learn the pathname in which the merged result will be stored via placeholder %P.




          More concretely, your driver will be called with %A expanding to, e.g., .mergetmpA12345, %B expanding to .mergetmpB12345, and %O expanding to .mergetmpO12345. This is true regardless of whether you're being called to merge three copies of README.txt, three copies of main.py, or three copies of pom.xml: the temporary file names will be .merge<something>.



          If you want, for whatever reason, to know that the "real name" of the file is README.txt or main.py or whatever, that is available in %P. Don't use that file now—that's neither an input nor an output. That's just the work-tree file's eventual name—it is, or will be, its (eventual) name, once the merge is all done. For the moment, though, the name you should use—the one where you should deliver your merged file data—is whatever %A expands to (some .merge<something>).



          Using git merge-file



          The git merge-file documentation tells us that the usage for git merge-file is:




          git merge-file [-L <current-name> [-L <base-name> [-L <other-name>]]]
          [--ours | --theirs | --union] [-p | --stdout] [-q | --quiet] [--marker-size=<n>]
          [--[no-]diff3] <current-file> <base-file> <other-file>




          The three -L options are labels that git merge-file will place around conflicted sections. The --marker-size option sets the size of the marker that git merge-file will place around conflicted sections. The defaults are pretty similar to Git's own internal merge, so a lot of the time, these are not needed. In your case, you're going to specify --ours, which means there will never be unresolved conflicts and therefore -L and --marker-size are not needed.



          The --ours or --theirs or --union flag tells git merge-file how to handle conflicts. Two of these flags are the same as the extended-options -X ours and -X theirs; you gain a third one that's not available in regular Git merge.



          The --diff3 or --no-diff3 options again affect how conflicts are shown: they are equivalent to setting merge.style to diff3 or to merge respectively. Since you won't have conflicts, these are irrelevant.



          Finally, you need to name three files: one containing the current ("ours") version data, one with the merge base version, and one with the other ("theirs") version. When git merge-file finishes, it will rewrite the current-version file with the merged data. Your git merge has, conveniently, built three such files, and uses them exactly this way, so you need only provide the %A %O %B options:



          *pom.xml merge=favor-ours


          and:



          [merge "favor-ours"]
          name = merge driver that merges but favors ours a la -X ours
          driver = git merge-file --ours %A %O %B


          Because of the --ours option, this merge will always succeed (git merge-file will exit 0), even if the result is not valid XML.






          share|improve this answer























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53429202%2fgit-recursive-merge-strategy-options-within-gitattributes%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0














            TL;DR



            Use git merge-file in your merge driver. Here, you can specify the options you want. You're asking for --ours. Be aware that this does not always work right. You might even want --union, but be aware that this does not really work right either. See Can git be made to mostly auto-merge XML order-insensitive files? and VS 2013 C# and Git: .csproj files don't merge upon pulling for issues with trying to use either method with XML files.



            Long



            First, let's define the terms the way Git uses them:





            • A strategy is the argument you pass to the -s flag to git merge: git merge -s recursive, git merge -s resolve, git merge -s ours, or git merge -s octopus. (In practice you would never use the last one explicitly—instead, it's implied if you ask Git to merge multiple heads.)



              In other words, this is one of the names ours, recursive, or resolve. In theory, you can write your own Git merge strategy as well, but that's very non-trivial (so much so that I have not heard of anyone doing it, though this might not mean that much).



            • A strategy option the argument you pass to the -X flag, such as -X ours or -X rename-threshold=<number>. I find this to be a bad name, as it's too easy to confuse with the strategy. This is especially true when speaking: did you say "strategy" and then stop speaking but mean "strategy option" and you were just interrupted, or did you say "strategy" and stop speaking and actually mean "strategy"? I like to call these extended options, which goes well with the -X flag, which then stands for "eXtended".)


            • A driver is something you define in a .gitattributes file. There are several flavors (filter, diff, and merge); here we're concerned with merge drivers. Merge drivers are invoked by a strategy; the driver could, in theory, have access to at least some of the extended-options, but in practice they don't. (Git should define %-expanders that provide them, and/or provide them in an environment variable; this would be relatively straightforward.)



            With these definitions in mind, it's easy to see that this:




            I'm struggling with forcing a specific merge strategy (-s recursive -Xours) for certain files within Git's .gitattributes file.




            is literally impossible. That's true whether you mean strategy or strategy option here: both have been chosen long before we get to the merge driver defined in the .gitattributes file.



            All is not necessarily lost, though!



            You mention specifically pom.xml, for which you want a merge driver—the thing you can define in .gitattributes—that does a text style merge but takes "ours" if there is a conflict, a la the extended-option "ours". What you have, instead, is a merge driver that will do an "ours" strategy merge: ignore both the merge base and their changes entirely, just taking "our" version of the file.



            Compare your attempt with the answer at Whats the Proper usage of .gitattributes with merge=ours: the only difference is the spelling of the driver name, where you use recursive instead of ours. The name of the driver is not important, as long as it's not one of the known magic names (text, binary, and union): Git will run your driver instead of doing its own file-level merge, when it determines that there is a file-level merge to be performed.



            The gitattributes documentation has this to say about defining a merge driver:




            The definition of a merge driver is done in the .git/config file, not in the gitattributes file, so strictly speaking this manual page is a wrong place to talk about it. However...



            To define a custom merge driver filfre, add a section to your $GIT_DIR/config file (or $HOME/.gitconfig file) like this:



            [merge "filfre"]
            name = feel-free merge driver
            driver = filfre %O %A %B %L %P
            recursive = binary



            (Side note: "recursive = binary" is not necessarily good advice, but let's press on here for now.)




            The merge.*.name variable gives the driver a human-readable name.



            The merge.*.driver variable’s value is used to construct a command to run to merge ancestor’s version (%O), current version (%A) and the other branches’ version (%B). These three tokens are replaced with the names of temporary files that hold the contents of these versions when the command line is built. Additionally, %L will be replaced with the conflict marker size (see below).




            That is, %O names a temporary file containing the merge-base version of the file, while %A and %B name temporary files containing the --ours and --theirs versions of the file. Your job, as filter-driver-writer, is to come up with a command that will perform the merge:




            The merge driver is expected to leave the result of the merge in the file named with %A by overwriting it, and exit with zero status if it managed to merge them cleanly, or non-zero if there were conflicts.




            Having performed the merge, you should overwrite the %A-named file with the result. Since the %A-named file contains the --ours contents, a driver that does nothing, but then exits, declares that the correct contents for merging %O vs %A and %B is whatever is already in %A.



            In other words, this is like the ours strategy. It is not like the merge-favoring-ours-in-case-of-conflicts, resolve-or-recursive-with--Xours strategy-plus-option. But you don't want a whole strategy at all, as that deals with entire commits; you want a single file operation. This is where git merge-file comes in. Let's finish off the rest of the description now, though:




            The merge.*.recursive variable specifies what other merge driver to use when the merge driver is called for an internal merge between common ancestors, when there are more than one. When left unspecified, the driver itself is used for both internal merge and the final merge.




            This is a bit confusing, but it applies only to the recursive strategy, and even then, only if git merge-base --all HEAD <theirs> prints out multiple hash IDs. Most of the time, you don't need anything special here at all. Using recursive = binary is equivalent to setting, for this one file, the strategy to resolve, because the binary internal merge driver just takes the ours version, which means that when -s recursive repeatedly merges merge bases, it just gets the first merge base version back each time.



            Multiple merge bases are rare, and it's not always clear what to do with them, but omitting any setting will cause the internal recursive merges to use your merge driver, which is probably fine for most cases. Your driver won't be called recursively; the recursion happens outside the calls to your driver. If you view the recursion as a tree, your driver is, in effect, called as a post-order traversal.




            The merge driver can learn the pathname in which the merged result will be stored via placeholder %P.




            More concretely, your driver will be called with %A expanding to, e.g., .mergetmpA12345, %B expanding to .mergetmpB12345, and %O expanding to .mergetmpO12345. This is true regardless of whether you're being called to merge three copies of README.txt, three copies of main.py, or three copies of pom.xml: the temporary file names will be .merge<something>.



            If you want, for whatever reason, to know that the "real name" of the file is README.txt or main.py or whatever, that is available in %P. Don't use that file now—that's neither an input nor an output. That's just the work-tree file's eventual name—it is, or will be, its (eventual) name, once the merge is all done. For the moment, though, the name you should use—the one where you should deliver your merged file data—is whatever %A expands to (some .merge<something>).



            Using git merge-file



            The git merge-file documentation tells us that the usage for git merge-file is:




            git merge-file [-L <current-name> [-L <base-name> [-L <other-name>]]]
            [--ours | --theirs | --union] [-p | --stdout] [-q | --quiet] [--marker-size=<n>]
            [--[no-]diff3] <current-file> <base-file> <other-file>




            The three -L options are labels that git merge-file will place around conflicted sections. The --marker-size option sets the size of the marker that git merge-file will place around conflicted sections. The defaults are pretty similar to Git's own internal merge, so a lot of the time, these are not needed. In your case, you're going to specify --ours, which means there will never be unresolved conflicts and therefore -L and --marker-size are not needed.



            The --ours or --theirs or --union flag tells git merge-file how to handle conflicts. Two of these flags are the same as the extended-options -X ours and -X theirs; you gain a third one that's not available in regular Git merge.



            The --diff3 or --no-diff3 options again affect how conflicts are shown: they are equivalent to setting merge.style to diff3 or to merge respectively. Since you won't have conflicts, these are irrelevant.



            Finally, you need to name three files: one containing the current ("ours") version data, one with the merge base version, and one with the other ("theirs") version. When git merge-file finishes, it will rewrite the current-version file with the merged data. Your git merge has, conveniently, built three such files, and uses them exactly this way, so you need only provide the %A %O %B options:



            *pom.xml merge=favor-ours


            and:



            [merge "favor-ours"]
            name = merge driver that merges but favors ours a la -X ours
            driver = git merge-file --ours %A %O %B


            Because of the --ours option, this merge will always succeed (git merge-file will exit 0), even if the result is not valid XML.






            share|improve this answer




























              0














              TL;DR



              Use git merge-file in your merge driver. Here, you can specify the options you want. You're asking for --ours. Be aware that this does not always work right. You might even want --union, but be aware that this does not really work right either. See Can git be made to mostly auto-merge XML order-insensitive files? and VS 2013 C# and Git: .csproj files don't merge upon pulling for issues with trying to use either method with XML files.



              Long



              First, let's define the terms the way Git uses them:





              • A strategy is the argument you pass to the -s flag to git merge: git merge -s recursive, git merge -s resolve, git merge -s ours, or git merge -s octopus. (In practice you would never use the last one explicitly—instead, it's implied if you ask Git to merge multiple heads.)



                In other words, this is one of the names ours, recursive, or resolve. In theory, you can write your own Git merge strategy as well, but that's very non-trivial (so much so that I have not heard of anyone doing it, though this might not mean that much).



              • A strategy option the argument you pass to the -X flag, such as -X ours or -X rename-threshold=<number>. I find this to be a bad name, as it's too easy to confuse with the strategy. This is especially true when speaking: did you say "strategy" and then stop speaking but mean "strategy option" and you were just interrupted, or did you say "strategy" and stop speaking and actually mean "strategy"? I like to call these extended options, which goes well with the -X flag, which then stands for "eXtended".)


              • A driver is something you define in a .gitattributes file. There are several flavors (filter, diff, and merge); here we're concerned with merge drivers. Merge drivers are invoked by a strategy; the driver could, in theory, have access to at least some of the extended-options, but in practice they don't. (Git should define %-expanders that provide them, and/or provide them in an environment variable; this would be relatively straightforward.)



              With these definitions in mind, it's easy to see that this:




              I'm struggling with forcing a specific merge strategy (-s recursive -Xours) for certain files within Git's .gitattributes file.




              is literally impossible. That's true whether you mean strategy or strategy option here: both have been chosen long before we get to the merge driver defined in the .gitattributes file.



              All is not necessarily lost, though!



              You mention specifically pom.xml, for which you want a merge driver—the thing you can define in .gitattributes—that does a text style merge but takes "ours" if there is a conflict, a la the extended-option "ours". What you have, instead, is a merge driver that will do an "ours" strategy merge: ignore both the merge base and their changes entirely, just taking "our" version of the file.



              Compare your attempt with the answer at Whats the Proper usage of .gitattributes with merge=ours: the only difference is the spelling of the driver name, where you use recursive instead of ours. The name of the driver is not important, as long as it's not one of the known magic names (text, binary, and union): Git will run your driver instead of doing its own file-level merge, when it determines that there is a file-level merge to be performed.



              The gitattributes documentation has this to say about defining a merge driver:




              The definition of a merge driver is done in the .git/config file, not in the gitattributes file, so strictly speaking this manual page is a wrong place to talk about it. However...



              To define a custom merge driver filfre, add a section to your $GIT_DIR/config file (or $HOME/.gitconfig file) like this:



              [merge "filfre"]
              name = feel-free merge driver
              driver = filfre %O %A %B %L %P
              recursive = binary



              (Side note: "recursive = binary" is not necessarily good advice, but let's press on here for now.)




              The merge.*.name variable gives the driver a human-readable name.



              The merge.*.driver variable’s value is used to construct a command to run to merge ancestor’s version (%O), current version (%A) and the other branches’ version (%B). These three tokens are replaced with the names of temporary files that hold the contents of these versions when the command line is built. Additionally, %L will be replaced with the conflict marker size (see below).




              That is, %O names a temporary file containing the merge-base version of the file, while %A and %B name temporary files containing the --ours and --theirs versions of the file. Your job, as filter-driver-writer, is to come up with a command that will perform the merge:




              The merge driver is expected to leave the result of the merge in the file named with %A by overwriting it, and exit with zero status if it managed to merge them cleanly, or non-zero if there were conflicts.




              Having performed the merge, you should overwrite the %A-named file with the result. Since the %A-named file contains the --ours contents, a driver that does nothing, but then exits, declares that the correct contents for merging %O vs %A and %B is whatever is already in %A.



              In other words, this is like the ours strategy. It is not like the merge-favoring-ours-in-case-of-conflicts, resolve-or-recursive-with--Xours strategy-plus-option. But you don't want a whole strategy at all, as that deals with entire commits; you want a single file operation. This is where git merge-file comes in. Let's finish off the rest of the description now, though:




              The merge.*.recursive variable specifies what other merge driver to use when the merge driver is called for an internal merge between common ancestors, when there are more than one. When left unspecified, the driver itself is used for both internal merge and the final merge.




              This is a bit confusing, but it applies only to the recursive strategy, and even then, only if git merge-base --all HEAD <theirs> prints out multiple hash IDs. Most of the time, you don't need anything special here at all. Using recursive = binary is equivalent to setting, for this one file, the strategy to resolve, because the binary internal merge driver just takes the ours version, which means that when -s recursive repeatedly merges merge bases, it just gets the first merge base version back each time.



              Multiple merge bases are rare, and it's not always clear what to do with them, but omitting any setting will cause the internal recursive merges to use your merge driver, which is probably fine for most cases. Your driver won't be called recursively; the recursion happens outside the calls to your driver. If you view the recursion as a tree, your driver is, in effect, called as a post-order traversal.




              The merge driver can learn the pathname in which the merged result will be stored via placeholder %P.




              More concretely, your driver will be called with %A expanding to, e.g., .mergetmpA12345, %B expanding to .mergetmpB12345, and %O expanding to .mergetmpO12345. This is true regardless of whether you're being called to merge three copies of README.txt, three copies of main.py, or three copies of pom.xml: the temporary file names will be .merge<something>.



              If you want, for whatever reason, to know that the "real name" of the file is README.txt or main.py or whatever, that is available in %P. Don't use that file now—that's neither an input nor an output. That's just the work-tree file's eventual name—it is, or will be, its (eventual) name, once the merge is all done. For the moment, though, the name you should use—the one where you should deliver your merged file data—is whatever %A expands to (some .merge<something>).



              Using git merge-file



              The git merge-file documentation tells us that the usage for git merge-file is:




              git merge-file [-L <current-name> [-L <base-name> [-L <other-name>]]]
              [--ours | --theirs | --union] [-p | --stdout] [-q | --quiet] [--marker-size=<n>]
              [--[no-]diff3] <current-file> <base-file> <other-file>




              The three -L options are labels that git merge-file will place around conflicted sections. The --marker-size option sets the size of the marker that git merge-file will place around conflicted sections. The defaults are pretty similar to Git's own internal merge, so a lot of the time, these are not needed. In your case, you're going to specify --ours, which means there will never be unresolved conflicts and therefore -L and --marker-size are not needed.



              The --ours or --theirs or --union flag tells git merge-file how to handle conflicts. Two of these flags are the same as the extended-options -X ours and -X theirs; you gain a third one that's not available in regular Git merge.



              The --diff3 or --no-diff3 options again affect how conflicts are shown: they are equivalent to setting merge.style to diff3 or to merge respectively. Since you won't have conflicts, these are irrelevant.



              Finally, you need to name three files: one containing the current ("ours") version data, one with the merge base version, and one with the other ("theirs") version. When git merge-file finishes, it will rewrite the current-version file with the merged data. Your git merge has, conveniently, built three such files, and uses them exactly this way, so you need only provide the %A %O %B options:



              *pom.xml merge=favor-ours


              and:



              [merge "favor-ours"]
              name = merge driver that merges but favors ours a la -X ours
              driver = git merge-file --ours %A %O %B


              Because of the --ours option, this merge will always succeed (git merge-file will exit 0), even if the result is not valid XML.






              share|improve this answer


























                0












                0








                0







                TL;DR



                Use git merge-file in your merge driver. Here, you can specify the options you want. You're asking for --ours. Be aware that this does not always work right. You might even want --union, but be aware that this does not really work right either. See Can git be made to mostly auto-merge XML order-insensitive files? and VS 2013 C# and Git: .csproj files don't merge upon pulling for issues with trying to use either method with XML files.



                Long



                First, let's define the terms the way Git uses them:





                • A strategy is the argument you pass to the -s flag to git merge: git merge -s recursive, git merge -s resolve, git merge -s ours, or git merge -s octopus. (In practice you would never use the last one explicitly—instead, it's implied if you ask Git to merge multiple heads.)



                  In other words, this is one of the names ours, recursive, or resolve. In theory, you can write your own Git merge strategy as well, but that's very non-trivial (so much so that I have not heard of anyone doing it, though this might not mean that much).



                • A strategy option the argument you pass to the -X flag, such as -X ours or -X rename-threshold=<number>. I find this to be a bad name, as it's too easy to confuse with the strategy. This is especially true when speaking: did you say "strategy" and then stop speaking but mean "strategy option" and you were just interrupted, or did you say "strategy" and stop speaking and actually mean "strategy"? I like to call these extended options, which goes well with the -X flag, which then stands for "eXtended".)


                • A driver is something you define in a .gitattributes file. There are several flavors (filter, diff, and merge); here we're concerned with merge drivers. Merge drivers are invoked by a strategy; the driver could, in theory, have access to at least some of the extended-options, but in practice they don't. (Git should define %-expanders that provide them, and/or provide them in an environment variable; this would be relatively straightforward.)



                With these definitions in mind, it's easy to see that this:




                I'm struggling with forcing a specific merge strategy (-s recursive -Xours) for certain files within Git's .gitattributes file.




                is literally impossible. That's true whether you mean strategy or strategy option here: both have been chosen long before we get to the merge driver defined in the .gitattributes file.



                All is not necessarily lost, though!



                You mention specifically pom.xml, for which you want a merge driver—the thing you can define in .gitattributes—that does a text style merge but takes "ours" if there is a conflict, a la the extended-option "ours". What you have, instead, is a merge driver that will do an "ours" strategy merge: ignore both the merge base and their changes entirely, just taking "our" version of the file.



                Compare your attempt with the answer at Whats the Proper usage of .gitattributes with merge=ours: the only difference is the spelling of the driver name, where you use recursive instead of ours. The name of the driver is not important, as long as it's not one of the known magic names (text, binary, and union): Git will run your driver instead of doing its own file-level merge, when it determines that there is a file-level merge to be performed.



                The gitattributes documentation has this to say about defining a merge driver:




                The definition of a merge driver is done in the .git/config file, not in the gitattributes file, so strictly speaking this manual page is a wrong place to talk about it. However...



                To define a custom merge driver filfre, add a section to your $GIT_DIR/config file (or $HOME/.gitconfig file) like this:



                [merge "filfre"]
                name = feel-free merge driver
                driver = filfre %O %A %B %L %P
                recursive = binary



                (Side note: "recursive = binary" is not necessarily good advice, but let's press on here for now.)




                The merge.*.name variable gives the driver a human-readable name.



                The merge.*.driver variable’s value is used to construct a command to run to merge ancestor’s version (%O), current version (%A) and the other branches’ version (%B). These three tokens are replaced with the names of temporary files that hold the contents of these versions when the command line is built. Additionally, %L will be replaced with the conflict marker size (see below).




                That is, %O names a temporary file containing the merge-base version of the file, while %A and %B name temporary files containing the --ours and --theirs versions of the file. Your job, as filter-driver-writer, is to come up with a command that will perform the merge:




                The merge driver is expected to leave the result of the merge in the file named with %A by overwriting it, and exit with zero status if it managed to merge them cleanly, or non-zero if there were conflicts.




                Having performed the merge, you should overwrite the %A-named file with the result. Since the %A-named file contains the --ours contents, a driver that does nothing, but then exits, declares that the correct contents for merging %O vs %A and %B is whatever is already in %A.



                In other words, this is like the ours strategy. It is not like the merge-favoring-ours-in-case-of-conflicts, resolve-or-recursive-with--Xours strategy-plus-option. But you don't want a whole strategy at all, as that deals with entire commits; you want a single file operation. This is where git merge-file comes in. Let's finish off the rest of the description now, though:




                The merge.*.recursive variable specifies what other merge driver to use when the merge driver is called for an internal merge between common ancestors, when there are more than one. When left unspecified, the driver itself is used for both internal merge and the final merge.




                This is a bit confusing, but it applies only to the recursive strategy, and even then, only if git merge-base --all HEAD <theirs> prints out multiple hash IDs. Most of the time, you don't need anything special here at all. Using recursive = binary is equivalent to setting, for this one file, the strategy to resolve, because the binary internal merge driver just takes the ours version, which means that when -s recursive repeatedly merges merge bases, it just gets the first merge base version back each time.



                Multiple merge bases are rare, and it's not always clear what to do with them, but omitting any setting will cause the internal recursive merges to use your merge driver, which is probably fine for most cases. Your driver won't be called recursively; the recursion happens outside the calls to your driver. If you view the recursion as a tree, your driver is, in effect, called as a post-order traversal.




                The merge driver can learn the pathname in which the merged result will be stored via placeholder %P.




                More concretely, your driver will be called with %A expanding to, e.g., .mergetmpA12345, %B expanding to .mergetmpB12345, and %O expanding to .mergetmpO12345. This is true regardless of whether you're being called to merge three copies of README.txt, three copies of main.py, or three copies of pom.xml: the temporary file names will be .merge<something>.



                If you want, for whatever reason, to know that the "real name" of the file is README.txt or main.py or whatever, that is available in %P. Don't use that file now—that's neither an input nor an output. That's just the work-tree file's eventual name—it is, or will be, its (eventual) name, once the merge is all done. For the moment, though, the name you should use—the one where you should deliver your merged file data—is whatever %A expands to (some .merge<something>).



                Using git merge-file



                The git merge-file documentation tells us that the usage for git merge-file is:




                git merge-file [-L <current-name> [-L <base-name> [-L <other-name>]]]
                [--ours | --theirs | --union] [-p | --stdout] [-q | --quiet] [--marker-size=<n>]
                [--[no-]diff3] <current-file> <base-file> <other-file>




                The three -L options are labels that git merge-file will place around conflicted sections. The --marker-size option sets the size of the marker that git merge-file will place around conflicted sections. The defaults are pretty similar to Git's own internal merge, so a lot of the time, these are not needed. In your case, you're going to specify --ours, which means there will never be unresolved conflicts and therefore -L and --marker-size are not needed.



                The --ours or --theirs or --union flag tells git merge-file how to handle conflicts. Two of these flags are the same as the extended-options -X ours and -X theirs; you gain a third one that's not available in regular Git merge.



                The --diff3 or --no-diff3 options again affect how conflicts are shown: they are equivalent to setting merge.style to diff3 or to merge respectively. Since you won't have conflicts, these are irrelevant.



                Finally, you need to name three files: one containing the current ("ours") version data, one with the merge base version, and one with the other ("theirs") version. When git merge-file finishes, it will rewrite the current-version file with the merged data. Your git merge has, conveniently, built three such files, and uses them exactly this way, so you need only provide the %A %O %B options:



                *pom.xml merge=favor-ours


                and:



                [merge "favor-ours"]
                name = merge driver that merges but favors ours a la -X ours
                driver = git merge-file --ours %A %O %B


                Because of the --ours option, this merge will always succeed (git merge-file will exit 0), even if the result is not valid XML.






                share|improve this answer













                TL;DR



                Use git merge-file in your merge driver. Here, you can specify the options you want. You're asking for --ours. Be aware that this does not always work right. You might even want --union, but be aware that this does not really work right either. See Can git be made to mostly auto-merge XML order-insensitive files? and VS 2013 C# and Git: .csproj files don't merge upon pulling for issues with trying to use either method with XML files.



                Long



                First, let's define the terms the way Git uses them:





                • A strategy is the argument you pass to the -s flag to git merge: git merge -s recursive, git merge -s resolve, git merge -s ours, or git merge -s octopus. (In practice you would never use the last one explicitly—instead, it's implied if you ask Git to merge multiple heads.)



                  In other words, this is one of the names ours, recursive, or resolve. In theory, you can write your own Git merge strategy as well, but that's very non-trivial (so much so that I have not heard of anyone doing it, though this might not mean that much).



                • A strategy option the argument you pass to the -X flag, such as -X ours or -X rename-threshold=<number>. I find this to be a bad name, as it's too easy to confuse with the strategy. This is especially true when speaking: did you say "strategy" and then stop speaking but mean "strategy option" and you were just interrupted, or did you say "strategy" and stop speaking and actually mean "strategy"? I like to call these extended options, which goes well with the -X flag, which then stands for "eXtended".)


                • A driver is something you define in a .gitattributes file. There are several flavors (filter, diff, and merge); here we're concerned with merge drivers. Merge drivers are invoked by a strategy; the driver could, in theory, have access to at least some of the extended-options, but in practice they don't. (Git should define %-expanders that provide them, and/or provide them in an environment variable; this would be relatively straightforward.)



                With these definitions in mind, it's easy to see that this:




                I'm struggling with forcing a specific merge strategy (-s recursive -Xours) for certain files within Git's .gitattributes file.




                is literally impossible. That's true whether you mean strategy or strategy option here: both have been chosen long before we get to the merge driver defined in the .gitattributes file.



                All is not necessarily lost, though!



                You mention specifically pom.xml, for which you want a merge driver—the thing you can define in .gitattributes—that does a text style merge but takes "ours" if there is a conflict, a la the extended-option "ours". What you have, instead, is a merge driver that will do an "ours" strategy merge: ignore both the merge base and their changes entirely, just taking "our" version of the file.



                Compare your attempt with the answer at Whats the Proper usage of .gitattributes with merge=ours: the only difference is the spelling of the driver name, where you use recursive instead of ours. The name of the driver is not important, as long as it's not one of the known magic names (text, binary, and union): Git will run your driver instead of doing its own file-level merge, when it determines that there is a file-level merge to be performed.



                The gitattributes documentation has this to say about defining a merge driver:




                The definition of a merge driver is done in the .git/config file, not in the gitattributes file, so strictly speaking this manual page is a wrong place to talk about it. However...



                To define a custom merge driver filfre, add a section to your $GIT_DIR/config file (or $HOME/.gitconfig file) like this:



                [merge "filfre"]
                name = feel-free merge driver
                driver = filfre %O %A %B %L %P
                recursive = binary



                (Side note: "recursive = binary" is not necessarily good advice, but let's press on here for now.)




                The merge.*.name variable gives the driver a human-readable name.



                The merge.*.driver variable’s value is used to construct a command to run to merge ancestor’s version (%O), current version (%A) and the other branches’ version (%B). These three tokens are replaced with the names of temporary files that hold the contents of these versions when the command line is built. Additionally, %L will be replaced with the conflict marker size (see below).




                That is, %O names a temporary file containing the merge-base version of the file, while %A and %B name temporary files containing the --ours and --theirs versions of the file. Your job, as filter-driver-writer, is to come up with a command that will perform the merge:




                The merge driver is expected to leave the result of the merge in the file named with %A by overwriting it, and exit with zero status if it managed to merge them cleanly, or non-zero if there were conflicts.




                Having performed the merge, you should overwrite the %A-named file with the result. Since the %A-named file contains the --ours contents, a driver that does nothing, but then exits, declares that the correct contents for merging %O vs %A and %B is whatever is already in %A.



                In other words, this is like the ours strategy. It is not like the merge-favoring-ours-in-case-of-conflicts, resolve-or-recursive-with--Xours strategy-plus-option. But you don't want a whole strategy at all, as that deals with entire commits; you want a single file operation. This is where git merge-file comes in. Let's finish off the rest of the description now, though:




                The merge.*.recursive variable specifies what other merge driver to use when the merge driver is called for an internal merge between common ancestors, when there are more than one. When left unspecified, the driver itself is used for both internal merge and the final merge.




                This is a bit confusing, but it applies only to the recursive strategy, and even then, only if git merge-base --all HEAD <theirs> prints out multiple hash IDs. Most of the time, you don't need anything special here at all. Using recursive = binary is equivalent to setting, for this one file, the strategy to resolve, because the binary internal merge driver just takes the ours version, which means that when -s recursive repeatedly merges merge bases, it just gets the first merge base version back each time.



                Multiple merge bases are rare, and it's not always clear what to do with them, but omitting any setting will cause the internal recursive merges to use your merge driver, which is probably fine for most cases. Your driver won't be called recursively; the recursion happens outside the calls to your driver. If you view the recursion as a tree, your driver is, in effect, called as a post-order traversal.




                The merge driver can learn the pathname in which the merged result will be stored via placeholder %P.




                More concretely, your driver will be called with %A expanding to, e.g., .mergetmpA12345, %B expanding to .mergetmpB12345, and %O expanding to .mergetmpO12345. This is true regardless of whether you're being called to merge three copies of README.txt, three copies of main.py, or three copies of pom.xml: the temporary file names will be .merge<something>.



                If you want, for whatever reason, to know that the "real name" of the file is README.txt or main.py or whatever, that is available in %P. Don't use that file now—that's neither an input nor an output. That's just the work-tree file's eventual name—it is, or will be, its (eventual) name, once the merge is all done. For the moment, though, the name you should use—the one where you should deliver your merged file data—is whatever %A expands to (some .merge<something>).



                Using git merge-file



                The git merge-file documentation tells us that the usage for git merge-file is:




                git merge-file [-L <current-name> [-L <base-name> [-L <other-name>]]]
                [--ours | --theirs | --union] [-p | --stdout] [-q | --quiet] [--marker-size=<n>]
                [--[no-]diff3] <current-file> <base-file> <other-file>




                The three -L options are labels that git merge-file will place around conflicted sections. The --marker-size option sets the size of the marker that git merge-file will place around conflicted sections. The defaults are pretty similar to Git's own internal merge, so a lot of the time, these are not needed. In your case, you're going to specify --ours, which means there will never be unresolved conflicts and therefore -L and --marker-size are not needed.



                The --ours or --theirs or --union flag tells git merge-file how to handle conflicts. Two of these flags are the same as the extended-options -X ours and -X theirs; you gain a third one that's not available in regular Git merge.



                The --diff3 or --no-diff3 options again affect how conflicts are shown: they are equivalent to setting merge.style to diff3 or to merge respectively. Since you won't have conflicts, these are irrelevant.



                Finally, you need to name three files: one containing the current ("ours") version data, one with the merge base version, and one with the other ("theirs") version. When git merge-file finishes, it will rewrite the current-version file with the merged data. Your git merge has, conveniently, built three such files, and uses them exactly this way, so you need only provide the %A %O %B options:



                *pom.xml merge=favor-ours


                and:



                [merge "favor-ours"]
                name = merge driver that merges but favors ours a la -X ours
                driver = git merge-file --ours %A %O %B


                Because of the --ours option, this merge will always succeed (git merge-file will exit 0), even if the result is not valid XML.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 23 '18 at 0:45









                torektorek

                193k18239321




                193k18239321
































                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53429202%2fgit-recursive-merge-strategy-options-within-gitattributes%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Can a sorcerer learn a 5th-level spell early by creating spell slots using the Font of Magic feature?

                    Does disintegrating a polymorphed enemy still kill it after the 2018 errata?

                    A Topological Invariant for $pi_3(U(n))$