1/* 2 * GIT - The information manager from hell 3 * 4 * Copyright (C) Linus Torvalds, 2005 5 */ 6#include"cache.h" 7 8static int stage =0; 9 10static intread_one_entry(unsigned char*sha1,const char*base,int baselen,const char*pathname,unsigned mode) 11{ 12int len =strlen(pathname); 13unsigned int size =cache_entry_size(baselen + len); 14struct cache_entry *ce =malloc(size); 15 16memset(ce,0, size); 17 18 ce->ce_mode =create_ce_mode(mode); 19 ce->ce_flags =create_ce_flags(baselen + len, stage); 20memcpy(ce->name, base, baselen); 21memcpy(ce->name + baselen, pathname, len+1); 22memcpy(ce->sha1, sha1,20); 23returnadd_cache_entry(ce,1); 24} 25 26static intread_tree(unsigned char*sha1,const char*base,int baselen) 27{ 28void*buffer; 29unsigned long size; 30char type[20]; 31 32 buffer =read_sha1_file(sha1, type, &size); 33if(!buffer) 34return-1; 35if(strcmp(type,"tree")) 36return-1; 37while(size) { 38int len =strlen(buffer)+1; 39unsigned char*sha1 = buffer + len; 40char*path =strchr(buffer,' ')+1; 41unsigned int mode; 42 43if(size < len +20||sscanf(buffer,"%o", &mode) !=1) 44return-1; 45 46 buffer = sha1 +20; 47 size -= len +20; 48 49if(S_ISDIR(mode)) { 50int retval; 51int pathlen =strlen(path); 52char*newbase =malloc(baselen +1+ pathlen); 53memcpy(newbase, base, baselen); 54memcpy(newbase + baselen, path, pathlen); 55 newbase[baselen + pathlen] ='/'; 56 retval =read_tree(sha1, newbase, baselen + pathlen +1); 57free(newbase); 58if(retval) 59return-1; 60continue; 61} 62if(read_one_entry(sha1, base, baselen, path, mode) <0) 63return-1; 64} 65return0; 66} 67 68static int remove_lock =0; 69 70static voidremove_lock_file(void) 71{ 72if(remove_lock) 73unlink(".git/index.lock"); 74} 75 76static intpath_matches(struct cache_entry *a,struct cache_entry *b) 77{ 78int len =ce_namelen(a); 79returnce_namelen(b) == len && 80!memcmp(a->name, b->name, len); 81} 82 83static intsame(struct cache_entry *a,struct cache_entry *b) 84{ 85return a->ce_mode == b->ce_mode && 86!memcmp(a->sha1, b->sha1,20); 87} 88 89 90/* 91 * This removes all trivial merges that don't change the tree 92 * and collapses them to state 0. 93 * 94 * _Any_ other merge is left to user policy. That includes "both 95 * created the same file", and "both removed the same file" - which are 96 * trivial, but the user might still want to _note_ it. 97 */ 98static struct cache_entry *merge_entries(struct cache_entry *a, 99struct cache_entry *b, 100struct cache_entry *c) 101{ 102int len =ce_namelen(a); 103 104/* 105 * Are they all the same filename? We won't do 106 * any name merging 107 */ 108if(ce_namelen(b) != len || 109ce_namelen(c) != len || 110memcmp(a->name, b->name, len) || 111memcmp(a->name, c->name, len)) 112return NULL; 113 114/* 115 * Ok, all three entries describe the same 116 * filename, but maybe the contents or file 117 * mode have changed? 118 * 119 * The trivial cases end up being the ones where two 120 * out of three files are the same: 121 * - both destinations the same, trivially take either 122 * - one of the destination versions hasn't changed, 123 * take the other. 124 * 125 * The "all entries exactly the same" case falls out as 126 * a special case of any of the "two same" cases. 127 * 128 * Here "a" is "original", and "b" and "c" are the two 129 * trees we are merging. 130 */ 131if(same(b,c)) 132return c; 133if(same(a,b)) 134return c; 135if(same(a,c)) 136return b; 137return NULL; 138} 139 140static voidtrivially_merge_cache(struct cache_entry **src,int nr) 141{ 142static struct cache_entry null_entry; 143struct cache_entry **dst = src; 144struct cache_entry *old = &null_entry; 145 146while(nr) { 147struct cache_entry *ce, *result; 148 149 ce = src[0]; 150 151/* We throw away original cache entries except for the stat information */ 152if(!ce_stage(ce)) { 153 old = ce; 154 src++; 155 nr--; 156 active_nr--; 157continue; 158} 159if(nr >2&& (result =merge_entries(ce, src[1], src[2])) != NULL) { 160/* 161 * See if we can re-use the old CE directly? 162 * That way we get the uptodate stat info. 163 */ 164if(path_matches(result, old) &&same(result, old)) 165*result = *old; 166 ce = result; 167 ce->ce_flags &= ~htons(CE_STAGEMASK); 168 src +=2; 169 nr -=2; 170 active_nr -=2; 171} 172*dst++ = ce; 173 src++; 174 nr--; 175} 176} 177 178static voidmerge_stat_info(struct cache_entry **src,int nr) 179{ 180static struct cache_entry null_entry; 181struct cache_entry **dst = src; 182struct cache_entry *old = &null_entry; 183 184while(nr) { 185struct cache_entry *ce; 186 187 ce = src[0]; 188 189/* We throw away original cache entries except for the stat information */ 190if(!ce_stage(ce)) { 191 old = ce; 192 src++; 193 nr--; 194 active_nr--; 195continue; 196} 197if(path_matches(ce, old) &&same(ce, old)) 198*ce = *old; 199 ce->ce_flags &= ~htons(CE_STAGEMASK); 200*dst++ = ce; 201 src++; 202 nr--; 203} 204} 205 206intmain(int argc,char**argv) 207{ 208int i, newfd, merge; 209unsigned char sha1[20]; 210 211 newfd =open(".git/index.lock", O_RDWR | O_CREAT | O_EXCL,0600); 212if(newfd <0) 213die("unable to create new cachefile"); 214atexit(remove_lock_file); 215 remove_lock =1; 216 217 merge =0; 218for(i =1; i < argc; i++) { 219const char*arg = argv[i]; 220 221/* "-m" stands for "merge", meaning we start in stage 1 */ 222if(!strcmp(arg,"-m")) { 223int i; 224if(stage) 225usage("-m needs to come first"); 226read_cache(); 227for(i =0; i < active_nr; i++) { 228if(ce_stage(active_cache[i])) 229usage("you need to resolve your current index first"); 230} 231 stage =1; 232 merge =1; 233continue; 234} 235if(get_sha1_hex(arg, sha1) <0) 236usage("read-tree [-m] <sha1>"); 237if(stage >3) 238usage("can't merge more than two trees"); 239if(read_tree(sha1,"",0) <0) 240die("failed to unpack tree object%s", arg); 241 stage++; 242} 243if(merge) { 244switch(stage) { 245case4:/* Three-way merge */ 246trivially_merge_cache(active_cache, active_nr); 247break; 248case2:/* Just read a tree, merge with old cache contents */ 249merge_stat_info(active_cache, active_nr); 250break; 251default: 252die("just how do you expect me to merge%dtrees?", stage-1); 253} 254} 255if(write_cache(newfd, active_cache, active_nr) || 256rename(".git/index.lock",".git/index")) 257die("unable to write new index file"); 258 remove_lock =0; 259return0; 260}