git/merge-ort.c

/*
 * "Ostensibly Recursive's Twin" merge strategy, or "ort" for short.  Meant
 * as a drop-in replacement for the "recursive" merge strategy, allowing one
 * to replace
 *
 *   git merge [-s recursive]
 *
 * with
 *
 *   git merge -s ort
 *
 * Note: git's parser allows the space between '-s' and its argument to be
 * missing.  (Should I have backronymed "ham", "alsa", "kip", "nap, "alvo",
 * "cale", "peedy", or "ins" instead of "ort"?)
 */

#define USE_THE_REPOSITORY_VARIABLE

#include "git-compat-util.h"
#include "merge-ort.h"

#include "alloc.h"
#include "advice.h"
#include "attr.h"
#include "cache-tree.h"
#include "commit.h"
#include "commit-reach.h"
#include "diff.h"
#include "diffcore.h"
#include "dir.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
#include "entry.h"
#include "merge-ll.h"
#include "match-trees.h"
#include "mem-pool.h"
#include "object-name.h"
#include "object-store-ll.h"
#include "oid-array.h"
#include "path.h"
#include "promisor-remote.h"
#include "read-cache-ll.h"
#include "refs.h"
#include "revision.h"
#include "sparse-index.h"
#include "strmap.h"
#include "trace2.h"
#include "tree.h"
#include "unpack-trees.h"
#include "xdiff-interface.h"

/*
 * We have many arrays of size 3.  Whenever we have such an array, the
 * indices refer to one of the sides of the three-way merge.  This is so
 * pervasive that the constants 0, 1, and 2 are used in many places in the
 * code (especially in arithmetic operations to find the other side's index
 * or to compute a relevant mask), but sometimes these enum names are used
 * to aid code clarity.
 *
 * See also 'filemask' and 'dirmask' in struct conflict_info; the "ith side"
 * referred to there is one of these three sides.
 */
enum merge_side {};

static unsigned RESULT_INITIALIZED =; /* unlikely accidental value */

struct traversal_callback_data {};

struct deferred_traversal_data {};

struct rename_info {};

struct merge_options_internal {};

struct conflicted_submodule_item {};

static void conflicted_submodule_item_free(void *util, const char *str UNUSED)
{}

struct version_info {};

struct merged_info {};

struct conflict_info {};

enum conflict_and_info_types {};

/*
 * Short description of conflict type, relied upon by external tools.
 *
 * We can add more entries, but DO NOT change any of these strings.  Also,
 * please ensure the order matches what is used in conflict_info_and_types.
 */
static const char *type_short_descriptions[] =;

struct logical_conflict_info {};

/*** Function Grouping: various utility functions ***/

/*
 * For the next three macros, see warning for conflict_info.merged.
 *
 * In each of the below, mi is a struct merged_info*, and ci was defined
 * as a struct conflict_info* (but we need to verify ci isn't actually
 * pointed at a struct merged_info*).
 *
 * INITIALIZE_CI: Assign ci to mi but only if it's safe; set to NULL otherwise.
 * VERIFY_CI: Ensure that something we assigned to a conflict_info* is one.
 * ASSIGN_AND_VERIFY_CI: Similar to VERIFY_CI but do assignment first.
 */
#define INITIALIZE_CI(ci, mi)
#define VERIFY_CI(ci)
#define ASSIGN_AND_VERIFY_CI(ci, mi)

static void free_strmap_strings(struct strmap *map)
{}

static void clear_or_reinit_internal_opts(struct merge_options_internal *opti,
					  int reinitialize)
{}

static void format_commit(struct strbuf *sb,
			  int indent,
			  struct repository *repo,
			  struct commit *commit)
{}

__attribute__((format (printf, 8, 9)))
static void path_msg(struct merge_options *opt,
		     enum conflict_and_info_types type,
		     int omittable_hint, /* skippable under --remerge-diff */
		     const char *primary_path,
		     const char *other_path_1, /* may be NULL */
		     const char *other_path_2, /* may be NULL */
		     struct string_list *other_paths, /* may be NULL */
		     const char *fmt, ...)
{}

static struct diff_filespec *pool_alloc_filespec(struct mem_pool *pool,
						 const char *path)
{}

static struct diff_filepair *pool_diff_queue(struct mem_pool *pool,
					     struct diff_queue_struct *queue,
					     struct diff_filespec *one,
					     struct diff_filespec *two)
{}

/* add a string to a strbuf, but converting "/" to "_" */
static void add_flattened_path(struct strbuf *out, const char *s)
{}

static char *unique_path(struct merge_options *opt,
			 const char *path,
			 const char *branch)
{}

/*** Function Grouping: functions related to collect_merge_info() ***/

static int traverse_trees_wrapper_callback(int n,
					   unsigned long mask,
					   unsigned long dirmask,
					   struct name_entry *names,
					   struct traverse_info *info)
{}

/*
 * Much like traverse_trees(), BUT:
 *   - read all the tree entries FIRST, saving them
 *   - note that the above step provides an opportunity to compute necessary
 *     additional details before the "real" traversal
 *   - loop through the saved entries and call the original callback on them
 */
static int traverse_trees_wrapper(struct index_state *istate,
				  int n,
				  struct tree_desc *t,
				  struct traverse_info *info)
{}

static void setup_path_info(struct merge_options *opt,
			    struct string_list_item *result,
			    const char *current_dir_name,
			    int current_dir_name_len,
			    char *fullpath, /* we'll take over ownership */
			    struct name_entry *names,
			    struct name_entry *merged_version,
			    unsigned is_null,     /* boolean */
			    unsigned df_conflict, /* boolean */
			    unsigned filemask,
			    unsigned dirmask,
			    int resolved          /* boolean */)
{}

static void add_pair(struct merge_options *opt,
		     struct name_entry *names,
		     const char *pathname,
		     unsigned side,
		     unsigned is_add /* if false, is_delete */,
		     unsigned match_mask,
		     unsigned dir_rename_mask)
{}

static void collect_rename_info(struct merge_options *opt,
				struct name_entry *names,
				const char *dirname,
				const char *fullname,
				unsigned filemask,
				unsigned dirmask,
				unsigned match_mask)
{}

static int collect_merge_info_callback(int n,
				       unsigned long mask,
				       unsigned long dirmask,
				       struct name_entry *names,
				       struct traverse_info *info)
{}

static void resolve_trivial_directory_merge(struct conflict_info *ci, int side)
{}

static int handle_deferred_entries(struct merge_options *opt,
				   struct traverse_info *info)
{}

static int collect_merge_info(struct merge_options *opt,
			      struct tree *merge_base,
			      struct tree *side1,
			      struct tree *side2)
{}

/*** Function Grouping: functions related to threeway content merges ***/

static int find_first_merges(struct repository *repo,
			     const char *path,
			     struct commit *a,
			     struct commit *b,
			     struct object_array *result)
{}

static int merge_submodule(struct merge_options *opt,
			   const char *path,
			   const struct object_id *o,
			   const struct object_id *a,
			   const struct object_id *b,
			   struct object_id *result)
{}

static void initialize_attr_index(struct merge_options *opt)
{}

static int merge_3way(struct merge_options *opt,
		      const char *path,
		      const struct object_id *o,
		      const struct object_id *a,
		      const struct object_id *b,
		      const char *pathnames[3],
		      const int extra_marker_size,
		      mmbuffer_t *result_buf)
{}

static int handle_content_merge(struct merge_options *opt,
				const char *path,
				const struct version_info *o,
				const struct version_info *a,
				const struct version_info *b,
				const char *pathnames[3],
				const int extra_marker_size,
				struct version_info *result)
{}

/*** Function Grouping: functions related to detect_and_process_renames(), ***
 *** which are split into directory and regular rename detection sections. ***/

/*** Function Grouping: functions related to directory rename detection ***/

struct collision_info {};

/*
 * Return a new string that replaces the beginning portion (which matches
 * rename_info->key), with rename_info->util.new_dir.  In perl-speak:
 *   new_path_name = (old_path =~ s/rename_info->key/rename_info->value/);
 * NOTE:
 *   Caller must ensure that old_path starts with rename_info->key + '/'.
 */
static char *apply_dir_rename(struct strmap_entry *rename_info,
			      const char *old_path)
{}

static int path_in_way(struct strmap *paths, const char *path, unsigned side_mask)
{}

/*
 * See if there is a directory rename for path, and if there are any file
 * level conflicts on the given side for the renamed location.  If there is
 * a rename and there are no conflicts, return the new name.  Otherwise,
 * return NULL.
 */
static char *handle_path_level_conflicts(struct merge_options *opt,
					 const char *path,
					 unsigned side_index,
					 struct strmap_entry *rename_info,
					 struct strmap *collisions)
{}

static void get_provisional_directory_renames(struct merge_options *opt,
					      unsigned side,
					      int *clean)
{}

static void handle_directory_level_conflicts(struct merge_options *opt)
{}

static struct strmap_entry *check_dir_renamed(const char *path,
					      struct strmap *dir_renames)
{}

static void compute_collisions(struct strmap *collisions,
			       struct strmap *dir_renames,
			       struct diff_queue_struct *pairs)
{}

static void free_collisions(struct strmap *collisions)
{}

static char *check_for_directory_rename(struct merge_options *opt,
					const char *path,
					unsigned side_index,
					struct strmap *dir_renames,
					struct strmap *dir_rename_exclusions,
					struct strmap *collisions,
					int *clean_merge)
{}

static void apply_directory_rename_modifications(struct merge_options *opt,
						 struct diff_filepair *pair,
						 char *new_path)
{}

/*** Function Grouping: functions related to regular rename detection ***/

static int process_renames(struct merge_options *opt,
			   struct diff_queue_struct *renames)
{}

static inline int possible_side_renames(struct rename_info *renames,
					unsigned side_index)
{}

static inline int possible_renames(struct rename_info *renames)
{}

static void resolve_diffpair_statuses(struct diff_queue_struct *q)
{}

static void prune_cached_from_relevant(struct rename_info *renames,
				       unsigned side)
{}

static void use_cached_pairs(struct merge_options *opt,
			     struct strmap *cached_pairs,
			     struct diff_queue_struct *pairs)
{}

static void cache_new_pair(struct rename_info *renames,
			   int side,
			   char *old_path,
			   char *new_path,
			   int free_old_value)
{}

static void possibly_cache_new_pair(struct rename_info *renames,
				    struct diff_filepair *p,
				    unsigned side,
				    char *new_path)
{}

static int compare_pairs(const void *a_, const void *b_)
{}

/* Call diffcore_rename() to update deleted/added pairs into rename pairs */
static int detect_regular_renames(struct merge_options *opt,
				  unsigned side_index)
{}

/*
 * Get information of all renames which occurred in 'side_pairs', making use
 * of any implicit directory renames in side_dir_renames (also making use of
 * implicit directory renames rename_exclusions as needed by
 * check_for_directory_rename()).  Add all (updated) renames into result.
 */
static int collect_renames(struct merge_options *opt,
			   struct diff_queue_struct *result,
			   unsigned side_index,
			   struct strmap *collisions,
			   struct strmap *dir_renames_for_side,
			   struct strmap *rename_exclusions)
{}

static int detect_and_process_renames(struct merge_options *opt)
{}

/*** Function Grouping: functions related to process_entries() ***/

static int sort_dirs_next_to_their_children(const char *one, const char *two)
{}

static int read_oid_strbuf(struct merge_options *opt,
			   const struct object_id *oid,
			   struct strbuf *dst,
			   const char *path)
{}

static int blob_unchanged(struct merge_options *opt,
			  const struct version_info *base,
			  const struct version_info *side,
			  const char *path)
{}

struct directory_versions {};

static int tree_entry_order(const void *a_, const void *b_)
{}

static int write_tree(struct object_id *result_oid,
		      struct string_list *versions,
		      unsigned int offset,
		      size_t hash_size)
{}

static void record_entry_for_tree(struct directory_versions *dir_metadata,
				  const char *path,
				  struct merged_info *mi)
{}

static int write_completed_directory(struct merge_options *opt,
				     const char *new_directory_name,
				     struct directory_versions *info)
{}

/* Per entry merge function */
static int process_entry(struct merge_options *opt,
			 const char *path,
			 struct conflict_info *ci,
			 struct directory_versions *dir_metadata)
{}

static void prefetch_for_content_merges(struct merge_options *opt,
					struct string_list *plist)
{}

static int process_entries(struct merge_options *opt,
			   struct object_id *result_oid)
{}

/*** Function Grouping: functions related to merge_switch_to_result() ***/

static int checkout(struct merge_options *opt,
		    struct tree *prev,
		    struct tree *next)
{}

static int record_conflicted_index_entries(struct merge_options *opt)
{}

static void print_submodule_conflict_suggestion(struct string_list *csub) {}

void merge_display_update_messages(struct merge_options *opt,
				   int detailed,
				   struct merge_result *result)
{}

void merge_get_conflicted_files(struct merge_result *result,
				struct string_list *conflicted_files)
{}

void merge_switch_to_result(struct merge_options *opt,
			    struct tree *head,
			    struct merge_result *result,
			    int update_worktree_and_index,
			    int display_update_msgs)
{}

void merge_finalize(struct merge_options *opt,
		    struct merge_result *result)
{}

/*** Function Grouping: helper functions for merge_incore_*() ***/

static struct tree *shift_tree_object(struct repository *repo,
				      struct tree *one, struct tree *two,
				      const char *subtree_shift)
{}

static inline void set_commit_tree(struct commit *c, struct tree *t)
{}

static struct commit *make_virtual_commit(struct repository *repo,
					  struct tree *tree,
					  const char *comment)
{}

static void merge_start(struct merge_options *opt, struct merge_result *result)
{}

static void merge_check_renames_reusable(struct merge_result *result,
					 struct tree *merge_base,
					 struct tree *side1,
					 struct tree *side2)
{}

/*** Function Grouping: merge_incore_*() and their internal variants ***/

static void move_opt_priv_to_result_priv(struct merge_options *opt,
					 struct merge_result *result)
{}

/*
 * Originally from merge_trees_internal(); heavily adapted, though.
 */
static void merge_ort_nonrecursive_internal(struct merge_options *opt,
					    struct tree *merge_base,
					    struct tree *side1,
					    struct tree *side2,
					    struct merge_result *result)
{}

/*
 * Originally from merge_recursive_internal(); somewhat adapted, though.
 */
static void merge_ort_internal(struct merge_options *opt,
			       const struct commit_list *_merge_bases,
			       struct commit *h1,
			       struct commit *h2,
			       struct merge_result *result)
{}

void merge_incore_nonrecursive(struct merge_options *opt,
			       struct tree *merge_base,
			       struct tree *side1,
			       struct tree *side2,
			       struct merge_result *result)
{}

void merge_incore_recursive(struct merge_options *opt,
			    const struct commit_list *merge_bases,
			    struct commit *side1,
			    struct commit *side2,
			    struct merge_result *result)
{}