简体   繁体   中英

Git debug custom merge driver

I am working on creating a custom merge driver. Everything seems to be working under a number of use cases; however, in one specific one I am getting during a merge: "fatal: custom merge driver users lacks command line." which I can only assume means that my custom merge driver is exiting with something other than a 0.

I am programming the merge driver in node.js and it is installed as a global package on my machine. The .gitconfig and .gitattributes seem to be setup right since the merge driver is being used when I expect it to.

When I was doing my own testing (manually specifying 3 files) I was console.log some debug information, but it seems that during runtime with GIT merge these console messages are supressed. Is there anyway that I can see some of this debug information from my custom merge driver as git is actually using it?

TL;DR summary

You must have, in some Git configuration file(s), at least two lines that read, eg:

[merge "users"]
    any-variable-not-named-driver = any-value

but without a previous or subsequent line (in the same section of the same Git config file) of the form:

    driver = some string, preferably with %-escapes

Note that when anyone (including an external program) runs git somecommand , there are up to three such configuration files read. One is system-wide (usually /etc/gitconfig ), so that all users on any given machine see the same file with the same contents. One file is per-user, found in $HOME/.gitconfig or $XDG_CONFIG_HOME/git/config . The last file is per-repository, found in $GIT_DIR/config , where $GIT_DIR defaults to the first .git directory at or above the current working directory.

You must also have, in a .gitattributes file, at least one line that reads:

name-or-pattern    merge=users

And you must then invoke a merge that involves a file pathname that matches the name-or-pattern on the left.

Explanation

The message itself comes from ll-merge.c around line 192 :

 if (fn->cmdline == NULL) die("custom merge driver %s lacks command line.", fn->name); 

(Incidentally, "ll" here stands for "low level": this is the "low level merge" code, with low level merges being done on a per-file basis.) Due to other error checking, this particular condition— fn->cmdline == NULL —can only occur if there at least one occurrence of merge.users. whatever merge.users. whatever in any of the config files Git has read. (You could get some other errors in various other cases, but those are not the droids you are looking for errors you are seeing.)

Remember that there are three such files (system, global, and local). They are read in that order: system first, then global, then local. Any or all three of these files can contain lines that partially or completely define a custom merge driver; the settings from all three files get applied, in whatever order they occur, with later settings generally overriding earlier settings.

This is how and why local settings generally override global settings. There are a few cases where settings accumulate, rather than overriding. For custom merge drivers, the overall effect is a weird hybrid: the drivers —the name part from merge. name . whatever merge. name . whatever merge. name . whatever —accumulate, but the individual settings —the whatever part for any one given merge. name . whatever merge. name . whatever merge. name . whatever —get overridden, if they occur more than once!

Next, see read_merge_config() , lines 232-296 of the same file : these mean that whenever merge.users. whatever merge.users. whatever is read, a data structure of type struct ll_merge_driver is created if necessary, or—if it exists—the existing one with name users is re-used. The variable fn points to this structure; fn->name is users and fn->fn is ll_ext_merge() (the latter is the C code function that invokes an external merge driver, and is where the quoted two lines above are found).

Some of the remaining lines in read_merge_config() check whether the whatever is the literal string name or driver . For the latter case ( driver ), the code fills in the fn->cmdline field with the specified value.

To get ll_ext_merge() itself called at all with users as the name of the user-defined merge driver, these three conditions must be satisfied:

  • You have run (directly or indirectly) git merge or a command that invokes a merge (such as git cherry-pick or git revert or git rebase or git apply -3 , for instance);

  • Based on all three inputs (the merge base and the two tip commits), some file F requires a full three-way "low level" merge because it has three different hash IDs in the three inputs; and

  • The .gitattributes for file F includes the line merge=users .

At this time (and at no other time), Git actually calls ll_ext_merge , passing it the data structure created and filled in (whether partially or completely) while reading the three Git configuration files (system, global, and local).

Since you now get the error message that you got, we know:

  • There existed, in the three configuration files, at least one occurrence of merge.users. whatever merge.users. whatever , but
  • None of the whatever s was driver .

This is how I reached the conclusion at the top of this answer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM