/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_merge_h__ #define INCLUDE_merge_h__ #include "vector.h" #include "commit_list.h" #include "pool.h" #include "iterator.h" #include "git2/types.h" #include "git2/merge.h" #include "git2/sys/merge.h" #define GIT_MERGE_MSG_FILE "MERGE_MSG" #define GIT_MERGE_MODE_FILE "MERGE_MODE" #define GIT_MERGE_FILE_MODE 0666 #define GIT_MERGE_DEFAULT_RENAME_THRESHOLD 50 #define GIT_MERGE_DEFAULT_TARGET_LIMIT 1000 /** Internal merge flags. */ enum { /** The merge is for a virtual base in a recursive merge. */ GIT_MERGE__VIRTUAL_BASE = (1 << 31), }; enum { /** Accept the conflict file, staging it as the merge result. */ GIT_MERGE_FILE_FAVOR__CONFLICTED = 4, }; /** Types of changes when files are merged from branch to branch. */ typedef enum { /* No conflict - a change only occurs in one branch. */ GIT_MERGE_DIFF_NONE = 0, /* Occurs when a file is modified in both branches. */ GIT_MERGE_DIFF_BOTH_MODIFIED = (1 << 0), /* Occurs when a file is added in both branches. */ GIT_MERGE_DIFF_BOTH_ADDED = (1 << 1), /* Occurs when a file is deleted in both branches. */ GIT_MERGE_DIFF_BOTH_DELETED = (1 << 2), /* Occurs when a file is modified in one branch and deleted in the other. */ GIT_MERGE_DIFF_MODIFIED_DELETED = (1 << 3), /* Occurs when a file is renamed in one branch and modified in the other. */ GIT_MERGE_DIFF_RENAMED_MODIFIED = (1 << 4), /* Occurs when a file is renamed in one branch and deleted in the other. */ GIT_MERGE_DIFF_RENAMED_DELETED = (1 << 5), /* Occurs when a file is renamed in one branch and a file with the same * name is added in the other. Eg, A->B and new file B. Core git calls * this a "rename/delete". */ GIT_MERGE_DIFF_RENAMED_ADDED = (1 << 6), /* Occurs when both a file is renamed to the same name in the ours and * theirs branches. Eg, A->B and A->B in both. Automergeable. */ GIT_MERGE_DIFF_BOTH_RENAMED = (1 << 7), /* Occurs when a file is renamed to different names in the ours and theirs * branches. Eg, A->B and A->C. */ GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2 = (1 << 8), /* Occurs when two files are renamed to the same name in the ours and * theirs branches. Eg, A->C and B->C. */ GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 = (1 << 9), /* Occurs when an item at a path in one branch is a directory, and an * item at the same path in a different branch is a file. */ GIT_MERGE_DIFF_DIRECTORY_FILE = (1 << 10), /* The child of a folder that is in a directory/file conflict. */ GIT_MERGE_DIFF_DF_CHILD = (1 << 11), } git_merge_diff_type_t; typedef struct { git_repository *repo; git_pool pool; /* Vector of git_index_entry that represent the merged items that * have been staged, either because only one side changed, or because * the two changes were non-conflicting and mergeable. These items * will be written as staged entries in the main index. */ git_vector staged; /* Vector of git_merge_diff entries that represent the conflicts that * have not been automerged. These items will be written to high-stage * entries in the main index. */ git_vector conflicts; /* Vector of git_merge_diff that have been automerged. These items * will be written to the REUC when the index is produced. */ git_vector resolved; } git_merge_diff_list; /** * Description of changes to one file across three trees. */ typedef struct { git_merge_diff_type_t type; git_index_entry ancestor_entry; git_index_entry our_entry; git_delta_t our_status; git_index_entry their_entry; git_delta_t their_status; } git_merge_diff; int git_merge__bases_many( git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos); /* * Three-way tree differencing */ git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo); int git_merge_diff_list__find_differences( git_merge_diff_list *merge_diff_list, git_iterator *ancestor_iterator, git_iterator *ours_iter, git_iterator *theirs_iter); int git_merge_diff_list__find_renames(git_repository *repo, git_merge_diff_list *merge_diff_list, const git_merge_options *opts); void git_merge_diff_list__free(git_merge_diff_list *diff_list); /* Merge metadata setup */ int git_merge__setup( git_repository *repo, const git_annotated_commit *our_head, const git_annotated_commit *heads[], size_t heads_len); int git_merge__iterators( git_index **out, git_repository *repo, git_iterator *ancestor_iter, git_iterator *our_iter, git_iterator *their_iter, const git_merge_options *given_opts); int git_merge__check_result(git_repository *repo, git_index *index_new); int git_merge__append_conflicts_to_merge_msg(git_repository *repo, git_index *index); /* Merge files */ GIT_INLINE(const char *) git_merge_file__best_path( const char *ancestor, const char *ours, const char *theirs) { if (!ancestor) { if (ours && theirs && strcmp(ours, theirs) == 0) return ours; return NULL; } if (ours && strcmp(ancestor, ours) == 0) return theirs; else if(theirs && strcmp(ancestor, theirs) == 0) return ours; return NULL; } GIT_INLINE(uint32_t) git_merge_file__best_mode( uint32_t ancestor, uint32_t ours, uint32_t theirs) { /* * If ancestor didn't exist and either ours or theirs is executable, * assume executable. Otherwise, if any mode changed from the ancestor, * use that one. */ if (!ancestor) { if (ours == GIT_FILEMODE_BLOB_EXECUTABLE || theirs == GIT_FILEMODE_BLOB_EXECUTABLE) return GIT_FILEMODE_BLOB_EXECUTABLE; return GIT_FILEMODE_BLOB; } else if (ours && theirs) { if (ancestor == ours) return theirs; return ours; } return 0; } #endif