read-tree.con commit Make "read-tree" merge the trees it reads by giving them consecutive states. (d99082e)
   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 int read_one_entry(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode)
  11{
  12        int len = strlen(pathname);
  13        unsigned int size = cache_entry_size(baselen + len);
  14        struct cache_entry *ce = malloc(size);
  15
  16        memset(ce, 0, size);
  17
  18        ce->ce_mode = htonl(mode);
  19        ce->ce_flags = create_ce_flags(baselen + len, stage);
  20        memcpy(ce->name, base, baselen);
  21        memcpy(ce->name + baselen, pathname, len+1);
  22        memcpy(ce->sha1, sha1, 20);
  23        return add_cache_entry(ce, 1);
  24}
  25
  26static int read_tree(unsigned char *sha1, const char *base, int baselen)
  27{
  28        void *buffer;
  29        unsigned long size;
  30        char type[20];
  31
  32        buffer = read_sha1_file(sha1, type, &size);
  33        if (!buffer)
  34                return -1;
  35        if (strcmp(type, "tree"))
  36                return -1;
  37        while (size) {
  38                int len = strlen(buffer)+1;
  39                unsigned char *sha1 = buffer + len;
  40                char *path = strchr(buffer, ' ')+1;
  41                unsigned int mode;
  42
  43                if (size < len + 20 || sscanf(buffer, "%o", &mode) != 1)
  44                        return -1;
  45
  46                buffer = sha1 + 20;
  47                size -= len + 20;
  48
  49                if (S_ISDIR(mode)) {
  50                        int retval;
  51                        int pathlen = strlen(path);
  52                        char *newbase = malloc(baselen + 1 + pathlen);
  53                        memcpy(newbase, base, baselen);
  54                        memcpy(newbase + baselen, path, pathlen);
  55                        newbase[baselen + pathlen] = '/';
  56                        retval = read_tree(sha1, newbase, baselen + pathlen + 1);
  57                        free(newbase);
  58                        if (retval)
  59                                return -1;
  60                        continue;
  61                }
  62                if (read_one_entry(sha1, base, baselen, path, mode) < 0)
  63                        return -1;
  64        }
  65        return 0;
  66}
  67
  68static int remove_lock = 0;
  69
  70static void remove_lock_file(void)
  71{
  72        if (remove_lock)
  73                unlink(".git/index.lock");
  74}
  75
  76/*
  77 * This removes all identical entries and collapses them to state 0.
  78 *
  79 * _Any_ other merge (even a trivial one, like both ) is left to user policy.
  80 * That includes "both created the same file", and "both removed the same
  81 * file" - which are trivial, but the user might still want to _note_ it.
  82 */
  83static int same_entry(struct cache_entry *a,
  84                        struct cache_entry *b,
  85                        struct cache_entry *c)
  86{
  87        int len = ce_namelen(a);
  88        return  a->ce_mode == b->ce_mode &&
  89                a->ce_mode == c->ce_mode &&
  90                ce_namelen(b) == len &&
  91                ce_namelen(c) == len &&
  92                !memcmp(a->name, b->name, len) &&
  93                !memcmp(a->name, c->name, len) &&
  94                !memcmp(a->sha1, b->sha1, 20) &&
  95                !memcmp(a->sha1, c->sha1, 20);
  96}
  97
  98static void trivially_merge_cache(struct cache_entry **src, int nr)
  99{
 100        struct cache_entry **dst = src;
 101
 102        while (nr) {
 103                struct cache_entry *ce;
 104
 105                ce = src[0];
 106                if (nr > 2 && same_entry(ce, src[1], src[2])) {
 107                        ce->ce_flags &= ~htons(CE_STAGEMASK);
 108                        src += 2;
 109                        nr -= 2;
 110                        active_nr -= 2;
 111                }
 112                *dst = ce;
 113                src++;
 114                dst++;
 115                nr--;
 116        }
 117}
 118
 119int main(int argc, char **argv)
 120{
 121        int i, newfd;
 122        unsigned char sha1[20];
 123
 124        newfd = open(".git/index.lock", O_RDWR | O_CREAT | O_EXCL, 0600);
 125        if (newfd < 0)
 126                die("unable to create new cachefile");
 127        atexit(remove_lock_file);
 128        remove_lock = 1;
 129
 130        for (i = 1; i < argc; i++) {
 131                const char *arg = argv[i];
 132
 133                /* "-m" stands for "merge", meaning we start in stage 1 */
 134                if (!strcmp(arg, "-m")) {
 135                        stage = 1;
 136                        continue;
 137                }
 138                if (get_sha1_hex(arg, sha1) < 0)
 139                        usage("read-tree [-m] <sha1>");
 140                if (stage > 3)
 141                        usage("can't merge more than two trees");
 142                if (read_tree(sha1, "", 0) < 0)
 143                        die("failed to unpack tree object %s", arg);
 144                stage++;
 145        }
 146        if (stage == 4)
 147                trivially_merge_cache(active_cache, active_nr);
 148        if (write_cache(newfd, active_cache, active_nr) ||
 149            rename(".git/index.lock", ".git/index"))
 150                die("unable to write new index file");
 151        remove_lock = 0;
 152        return 0;
 153}