read-tree.con commit [PATCH] provide better committer information to commit-tree.c (b70070f)
   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 = create_ce_mode(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
  76static int same(struct cache_entry *a, struct cache_entry *b)
  77{
  78        return a->ce_mode == b->ce_mode && 
  79                !memcmp(a->sha1, b->sha1, 20);
  80}
  81
  82
  83/*
  84 * This removes all trivial merges that don't change the tree
  85 * and collapses them to state 0.
  86 *
  87 * _Any_ other merge is left to user policy.  That includes "both
  88 * created the same file", and "both removed the same file" - which are
  89 * trivial, but the user might still want to _note_ it. 
  90 */
  91static struct cache_entry *merge_entries(struct cache_entry *a,
  92                                         struct cache_entry *b,
  93                                         struct cache_entry *c)
  94{
  95        int len = ce_namelen(a);
  96
  97        /*
  98         * Are they all the same filename? We won't do
  99         * any name merging
 100         */
 101        if (ce_namelen(b) != len ||
 102            ce_namelen(c) != len ||
 103            memcmp(a->name, b->name, len) ||
 104            memcmp(a->name, c->name, len))
 105                return NULL;
 106
 107        /*
 108         * Ok, all three entries describe the same
 109         * filename, but maybe the contents or file
 110         * mode have changed?
 111         *
 112         * The trivial cases end up being the ones where two
 113         * out of three files are the same:
 114         *  - both destinations the same, trivially take either
 115         *  - one of the destination versions hasn't changed,
 116         *    take the other.
 117         *
 118         * The "all entries exactly the same" case falls out as
 119         * a special case of any of the "two same" cases.
 120         *
 121         * Here "a" is "original", and "b" and "c" are the two
 122         * trees we are merging.
 123         */
 124        if (same(b,c))
 125                return c;
 126        if (same(a,b))
 127                return c;
 128        if (same(a,c))
 129                return b;
 130        return NULL;
 131}
 132
 133static void trivially_merge_cache(struct cache_entry **src, int nr)
 134{
 135        struct cache_entry **dst = src;
 136
 137        while (nr) {
 138                struct cache_entry *ce, *result;
 139
 140                ce = src[0];
 141                if (nr > 2 && (result = merge_entries(ce, src[1], src[2])) != NULL) {
 142                        ce = result;
 143                        ce->ce_flags &= ~htons(CE_STAGEMASK);
 144                        src += 2;
 145                        nr -= 2;
 146                        active_nr -= 2;
 147                }
 148                *dst = ce;
 149                src++;
 150                dst++;
 151                nr--;
 152        }
 153}
 154
 155int main(int argc, char **argv)
 156{
 157        int i, newfd;
 158        unsigned char sha1[20];
 159
 160        newfd = open(".git/index.lock", O_RDWR | O_CREAT | O_EXCL, 0600);
 161        if (newfd < 0)
 162                die("unable to create new cachefile");
 163        atexit(remove_lock_file);
 164        remove_lock = 1;
 165
 166        for (i = 1; i < argc; i++) {
 167                const char *arg = argv[i];
 168
 169                /* "-m" stands for "merge", meaning we start in stage 1 */
 170                if (!strcmp(arg, "-m")) {
 171                        stage = 1;
 172                        continue;
 173                }
 174                if (get_sha1_hex(arg, sha1) < 0)
 175                        usage("read-tree [-m] <sha1>");
 176                if (stage > 3)
 177                        usage("can't merge more than two trees");
 178                if (read_tree(sha1, "", 0) < 0)
 179                        die("failed to unpack tree object %s", arg);
 180                stage++;
 181        }
 182        if (stage == 4)
 183                trivially_merge_cache(active_cache, active_nr);
 184        if (write_cache(newfd, active_cache, active_nr) ||
 185            rename(".git/index.lock", ".git/index"))
 186                die("unable to write new index file");
 187        remove_lock = 0;
 188        return 0;
 189}