Oui, il y a une base. (Note Side:.. Ce code a beaucoup changé depuis que je l'ai regardé il y a quelques années, je pris un peu de cela pour ma récente réponse cerise de choix, que vous avez lié ici)
Les deux git cherry-pick
et git revert
sont mis en œuvre par les mêmes fichiers sources (builtin/revert.c
et sequencer.c
). Comme vous le dites, la partie la plus difficile est de décider quoi faire pour la base de fusion. Dans votre exemple, nous annulons les diffs B
-to- C
. Voici le code source (en sequencer.c
), dépouillé quelque peu:
if (opts->action == REPLAY_REVERT) {
base = commit;
base_label = msg.label;
next = parent;
next_label = msg.parent_label;
strbuf_addstr(&msgbuf, "Revert \"");
strbuf_addstr(&msgbuf, msg.subject);
strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit ");
strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
if (commit->parents && commit->parents->next) {
strbuf_addstr(&msgbuf, ", reversing\nchanges made to ");
strbuf_addstr(&msgbuf, oid_to_hex(&parent->object.oid));
}
strbuf_addstr(&msgbuf, ".\n");
} else {
[c'est le cas écrémer, inclus juste pour être complet]
const char *p;
base = parent;
base_label = msg.parent_label;
next = commit;
next_label = msg.label;
Quand nous entrons ici, commit
points Les données pour C
et parent
pointent vers les données pour B
. L'affectation à la variable base
est ce qui définit la base de fusion, et next
-vs- base
est ce qu'il faut apporter. Pour Cherry-Pick, le parent de la validation (éventuellement choisi via -m
) est la base de fusion. Pour revenir, le commit lui-même est la base de fusion et le parent (encore possiblement de -m
) est quoi-à-bring-in.
L'autre façon d'obtenir le même effet (comme cela a été fait il y a de nombreuses années, et jusqu'à récemment, je pensais que cela était encore utilisé) est d'appliquer une validation inversée comme produite par git format-patch
. Dans ce cas, la version de base construit est le second hachage (la B
partie de la A..B
partie d'un diff textuel):
/*
* This represents a "patch" to a file, both metainfo changes
* such as creation/deletion, filemode and content changes represented
* as a series of fragments.
*/
struct patch {
[snip]
char old_sha1_prefix[41];
char new_sha1_prefix[41];
static void reverse_patches(struct patch *p)
{
[snip]
swap(p->old_sha1_prefix, p->new_sha1_prefix);
La fonction reverse_patches
est appelé après l'extraction du texte en une série de plaques, à savoir, après le code qui extrait les hachages des lignes index
, en insérant les parties A
et B
dans les anciens et nouveaux champs de préfixe. Ensuite (après reverse_patches
), lorsque vous appliquez réellement chaque patch, git utilise les anciennes et les nouvelles valeurs sha1 enregistrées pour simuler une fusion à 3 voies (si git am
est donné --3way
). Ainsi, en appliquant un patch de texte à l'envers, nous obtiendrions le nouveau fichier en tant que base et l'original en tant que cible, tout comme avec le code sequencer.c
.