summaryrefslogtreecommitdiff
path: root/merge-index.c
blob: c00a2b385a3373f88b9fd3f087d9681cdc46cbf6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include "cache.h"
#include "run-command.h"
#include "exec_cmd.h"

static const char *pgm;
static const char *arguments[9];
static int one_shot, quiet;
static int err;

static void run_program(void)
{
	struct child_process child;
	memset(&child, 0, sizeof(child));
	child.argv = arguments;
	if (run_command(&child)) {
		if (one_shot) {
			err++;
		} else {
			if (!quiet)
				die("merge program failed");
			exit(1);
		}
	}
}

static int merge_entry(int pos, const char *path)
{
	int found;

	if (pos >= active_nr)
		die("git merge-index: %s not in the cache", path);
	arguments[0] = pgm;
	arguments[1] = "";
	arguments[2] = "";
	arguments[3] = "";
	arguments[4] = path;
	arguments[5] = "";
	arguments[6] = "";
	arguments[7] = "";
	arguments[8] = NULL;
	found = 0;
	do {
		static char hexbuf[4][60];
		static char ownbuf[4][60];
		struct cache_entry *ce = active_cache[pos];
		int stage = ce_stage(ce);

		if (strcmp(ce->name, path))
			break;
		found++;
		strcpy(hexbuf[stage], sha1_to_hex(ce->sha1));
		sprintf(ownbuf[stage], "%o", ce->ce_mode);
		arguments[stage] = hexbuf[stage];
		arguments[stage + 4] = ownbuf[stage];
	} while (++pos < active_nr);
	if (!found)
		die("git merge-index: %s not in the cache", path);
	run_program();
	return found;
}

static void merge_file(const char *path)
{
	int pos = cache_name_pos(path, strlen(path));

	/*
	 * If it already exists in the cache as stage0, it's
	 * already merged and there is nothing to do.
	 */
	if (pos < 0)
		merge_entry(-pos-1, path);
}

static void merge_all(void)
{
	int i;
	for (i = 0; i < active_nr; i++) {
		struct cache_entry *ce = active_cache[i];
		if (!ce_stage(ce))
			continue;
		i += merge_entry(i, ce->name)-1;
	}
}

int main(int argc, char **argv)
{
	int i, force_file = 0;

	/* Without this we cannot rely on waitpid() to tell
	 * what happened to our children.
	 */
	signal(SIGCHLD, SIG_DFL);

	if (argc < 3)
		usage("git-merge-index [-o] [-q] <merge-program> (-a | [--] <filename>*)");

	git_extract_argv0_path(argv[0]);

	setup_git_directory();
	read_cache();

	i = 1;
	if (!strcmp(argv[i], "-o")) {
		one_shot = 1;
		i++;
	}
	if (!strcmp(argv[i], "-q")) {
		quiet = 1;
		i++;
	}
	pgm = argv[i++];
	for (; i < argc; i++) {
		char *arg = argv[i];
		if (!force_file && *arg == '-') {
			if (!strcmp(arg, "--")) {
				force_file = 1;
				continue;
			}
			if (!strcmp(arg, "-a")) {
				merge_all();
				continue;
			}
			die("git merge-index: unknown option %s", arg);
		}
		merge_file(arg);
	}
	if (err && !quiet)
		die("merge program failed");
	return err;
}