unpack-trees.con commit read-trees: refactor the unpack_trees() part (16da134)
   1#include <signal.h>
   2#include <sys/time.h>
   3#include "cache.h"
   4#include "tree.h"
   5#include "tree-walk.h"
   6#include "unpack-trees.h"
   7
   8struct tree_entry_list {
   9        struct tree_entry_list *next;
  10        unsigned directory : 1;
  11        unsigned executable : 1;
  12        unsigned symlink : 1;
  13        unsigned int mode;
  14        const char *name;
  15        const unsigned char *sha1;
  16};
  17
  18static struct tree_entry_list *create_tree_entry_list(struct tree *tree)
  19{
  20        struct tree_desc desc;
  21        struct name_entry one;
  22        struct tree_entry_list *ret = NULL;
  23        struct tree_entry_list **list_p = &ret;
  24
  25        desc.buf = tree->buffer;
  26        desc.size = tree->size;
  27
  28        while (tree_entry(&desc, &one)) {
  29                struct tree_entry_list *entry;
  30
  31                entry = xmalloc(sizeof(struct tree_entry_list));
  32                entry->name = one.path;
  33                entry->sha1 = one.sha1;
  34                entry->mode = one.mode;
  35                entry->directory = S_ISDIR(one.mode) != 0;
  36                entry->executable = (one.mode & S_IXUSR) != 0;
  37                entry->symlink = S_ISLNK(one.mode) != 0;
  38                entry->next = NULL;
  39
  40                *list_p = entry;
  41                list_p = &entry->next;
  42        }
  43        return ret;
  44}
  45
  46static int entcmp(const char *name1, int dir1, const char *name2, int dir2)
  47{
  48        int len1 = strlen(name1);
  49        int len2 = strlen(name2);
  50        int len = len1 < len2 ? len1 : len2;
  51        int ret = memcmp(name1, name2, len);
  52        unsigned char c1, c2;
  53        if (ret)
  54                return ret;
  55        c1 = name1[len];
  56        c2 = name2[len];
  57        if (!c1 && dir1)
  58                c1 = '/';
  59        if (!c2 && dir2)
  60                c2 = '/';
  61        ret = (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
  62        if (c1 && c2 && !ret)
  63                ret = len1 - len2;
  64        return ret;
  65}
  66
  67static int unpack_trees_rec(struct tree_entry_list **posns, int len,
  68                            const char *base, struct unpack_trees_options *o,
  69                            int *indpos,
  70                            struct tree_entry_list *df_conflict_list)
  71{
  72        int baselen = strlen(base);
  73        int src_size = len + 1;
  74        do {
  75                int i;
  76                const char *first;
  77                int firstdir = 0;
  78                int pathlen;
  79                unsigned ce_size;
  80                struct tree_entry_list **subposns;
  81                struct cache_entry **src;
  82                int any_files = 0;
  83                int any_dirs = 0;
  84                char *cache_name;
  85                int ce_stage;
  86
  87                /* Find the first name in the input. */
  88
  89                first = NULL;
  90                cache_name = NULL;
  91
  92                /* Check the cache */
  93                if (o->merge && *indpos < active_nr) {
  94                        /* This is a bit tricky: */
  95                        /* If the index has a subdirectory (with
  96                         * contents) as the first name, it'll get a
  97                         * filename like "foo/bar". But that's after
  98                         * "foo", so the entry in trees will get
  99                         * handled first, at which point we'll go into
 100                         * "foo", and deal with "bar" from the index,
 101                         * because the base will be "foo/". The only
 102                         * way we can actually have "foo/bar" first of
 103                         * all the things is if the trees don't
 104                         * contain "foo" at all, in which case we'll
 105                         * handle "foo/bar" without going into the
 106                         * directory, but that's fine (and will return
 107                         * an error anyway, with the added unknown
 108                         * file case.
 109                         */
 110
 111                        cache_name = active_cache[*indpos]->name;
 112                        if (strlen(cache_name) > baselen &&
 113                            !memcmp(cache_name, base, baselen)) {
 114                                cache_name += baselen;
 115                                first = cache_name;
 116                        } else {
 117                                cache_name = NULL;
 118                        }
 119                }
 120
 121#if DBRT_DEBUG > 1
 122                if (first)
 123                        printf("index %s\n", first);
 124#endif
 125                for (i = 0; i < len; i++) {
 126                        if (!posns[i] || posns[i] == df_conflict_list)
 127                                continue;
 128#if DBRT_DEBUG > 1
 129                        printf("%d %s\n", i + 1, posns[i]->name);
 130#endif
 131                        if (!first || entcmp(first, firstdir,
 132                                             posns[i]->name,
 133                                             posns[i]->directory) > 0) {
 134                                first = posns[i]->name;
 135                                firstdir = posns[i]->directory;
 136                        }
 137                }
 138                /* No name means we're done */
 139                if (!first)
 140                        return 0;
 141
 142                pathlen = strlen(first);
 143                ce_size = cache_entry_size(baselen + pathlen);
 144
 145                src = xcalloc(src_size, sizeof(struct cache_entry *));
 146
 147                subposns = xcalloc(len, sizeof(struct tree_list_entry *));
 148
 149                if (cache_name && !strcmp(cache_name, first)) {
 150                        any_files = 1;
 151                        src[0] = active_cache[*indpos];
 152                        remove_cache_entry_at(*indpos);
 153                }
 154
 155                for (i = 0; i < len; i++) {
 156                        struct cache_entry *ce;
 157
 158                        if (!posns[i] ||
 159                            (posns[i] != df_conflict_list &&
 160                             strcmp(first, posns[i]->name))) {
 161                                continue;
 162                        }
 163
 164                        if (posns[i] == df_conflict_list) {
 165                                src[i + o->merge] = o->df_conflict_entry;
 166                                continue;
 167                        }
 168
 169                        if (posns[i]->directory) {
 170                                struct tree *tree = lookup_tree(posns[i]->sha1);
 171                                any_dirs = 1;
 172                                parse_tree(tree);
 173                                subposns[i] = create_tree_entry_list(tree);
 174                                posns[i] = posns[i]->next;
 175                                src[i + o->merge] = o->df_conflict_entry;
 176                                continue;
 177                        }
 178
 179                        if (!o->merge)
 180                                ce_stage = 0;
 181                        else if (i + 1 < o->head_idx)
 182                                ce_stage = 1;
 183                        else if (i + 1 > o->head_idx)
 184                                ce_stage = 3;
 185                        else
 186                                ce_stage = 2;
 187
 188                        ce = xcalloc(1, ce_size);
 189                        ce->ce_mode = create_ce_mode(posns[i]->mode);
 190                        ce->ce_flags = create_ce_flags(baselen + pathlen,
 191                                                       ce_stage);
 192                        memcpy(ce->name, base, baselen);
 193                        memcpy(ce->name + baselen, first, pathlen + 1);
 194
 195                        any_files = 1;
 196
 197                        memcpy(ce->sha1, posns[i]->sha1, 20);
 198                        src[i + o->merge] = ce;
 199                        subposns[i] = df_conflict_list;
 200                        posns[i] = posns[i]->next;
 201                }
 202                if (any_files) {
 203                        if (o->merge) {
 204                                int ret;
 205
 206#if DBRT_DEBUG > 1
 207                                printf("%s:\n", first);
 208                                for (i = 0; i < src_size; i++) {
 209                                        printf(" %d ", i);
 210                                        if (src[i])
 211                                                printf("%s\n", sha1_to_hex(src[i]->sha1));
 212                                        else
 213                                                printf("\n");
 214                                }
 215#endif
 216                                ret = o->fn(src, o);
 217
 218#if DBRT_DEBUG > 1
 219                                printf("Added %d entries\n", ret);
 220#endif
 221                                *indpos += ret;
 222                        } else {
 223                                for (i = 0; i < src_size; i++) {
 224                                        if (src[i]) {
 225                                                add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
 226                                        }
 227                                }
 228                        }
 229                }
 230                if (any_dirs) {
 231                        char *newbase = xmalloc(baselen + 2 + pathlen);
 232                        memcpy(newbase, base, baselen);
 233                        memcpy(newbase + baselen, first, pathlen);
 234                        newbase[baselen + pathlen] = '/';
 235                        newbase[baselen + pathlen + 1] = '\0';
 236                        if (unpack_trees_rec(subposns, len, newbase, o,
 237                                             indpos, df_conflict_list))
 238                                return -1;
 239                        free(newbase);
 240                }
 241                free(subposns);
 242                free(src);
 243        } while (1);
 244}
 245
 246/* Unlink the last component and attempt to remove leading
 247 * directories, in case this unlink is the removal of the
 248 * last entry in the directory -- empty directories are removed.
 249 */
 250static void unlink_entry(char *name)
 251{
 252        char *cp, *prev;
 253
 254        if (unlink(name))
 255                return;
 256        prev = NULL;
 257        while (1) {
 258                int status;
 259                cp = strrchr(name, '/');
 260                if (prev)
 261                        *prev = '/';
 262                if (!cp)
 263                        break;
 264
 265                *cp = 0;
 266                status = rmdir(name);
 267                if (status) {
 268                        *cp = '/';
 269                        break;
 270                }
 271                prev = cp;
 272        }
 273}
 274
 275static volatile int progress_update = 0;
 276
 277static void progress_interval(int signum)
 278{
 279        progress_update = 1;
 280}
 281
 282static void setup_progress_signal(void)
 283{
 284        struct sigaction sa;
 285        struct itimerval v;
 286
 287        memset(&sa, 0, sizeof(sa));
 288        sa.sa_handler = progress_interval;
 289        sigemptyset(&sa.sa_mask);
 290        sa.sa_flags = SA_RESTART;
 291        sigaction(SIGALRM, &sa, NULL);
 292
 293        v.it_interval.tv_sec = 1;
 294        v.it_interval.tv_usec = 0;
 295        v.it_value = v.it_interval;
 296        setitimer(ITIMER_REAL, &v, NULL);
 297}
 298
 299static struct checkout state;
 300static void check_updates(struct cache_entry **src, int nr,
 301                struct unpack_trees_options *o)
 302{
 303        unsigned short mask = htons(CE_UPDATE);
 304        unsigned last_percent = 200, cnt = 0, total = 0;
 305
 306        if (o->update && o->verbose_update) {
 307                for (total = cnt = 0; cnt < nr; cnt++) {
 308                        struct cache_entry *ce = src[cnt];
 309                        if (!ce->ce_mode || ce->ce_flags & mask)
 310                                total++;
 311                }
 312
 313                /* Don't bother doing this for very small updates */
 314                if (total < 250)
 315                        total = 0;
 316
 317                if (total) {
 318                        fprintf(stderr, "Checking files out...\n");
 319                        setup_progress_signal();
 320                        progress_update = 1;
 321                }
 322                cnt = 0;
 323        }
 324
 325        while (nr--) {
 326                struct cache_entry *ce = *src++;
 327
 328                if (total) {
 329                        if (!ce->ce_mode || ce->ce_flags & mask) {
 330                                unsigned percent;
 331                                cnt++;
 332                                percent = (cnt * 100) / total;
 333                                if (percent != last_percent ||
 334                                    progress_update) {
 335                                        fprintf(stderr, "%4u%% (%u/%u) done\r",
 336                                                percent, cnt, total);
 337                                        last_percent = percent;
 338                                        progress_update = 0;
 339                                }
 340                        }
 341                }
 342                if (!ce->ce_mode) {
 343                        if (o->update)
 344                                unlink_entry(ce->name);
 345                        continue;
 346                }
 347                if (ce->ce_flags & mask) {
 348                        ce->ce_flags &= ~mask;
 349                        if (o->update)
 350                                checkout_entry(ce, &state, NULL);
 351                }
 352        }
 353        if (total) {
 354                signal(SIGALRM, SIG_IGN);
 355                fputc('\n', stderr);
 356        }
 357}
 358
 359int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
 360{
 361        int indpos = 0;
 362        unsigned len = object_list_length(trees);
 363        struct tree_entry_list **posns;
 364        int i;
 365        struct object_list *posn = trees;
 366        struct tree_entry_list df_conflict_list;
 367        struct cache_entry df_conflict_entry;
 368
 369        memset(&df_conflict_list, 0, sizeof(df_conflict_list));
 370        df_conflict_list.next = &df_conflict_list;
 371        state.base_dir = "";
 372        state.force = 1;
 373        state.quiet = 1;
 374        state.refresh_cache = 1;
 375
 376        o->merge_size = len;
 377        o->df_conflict_entry = &df_conflict_entry;
 378
 379        if (len) {
 380                posns = xmalloc(len * sizeof(struct tree_entry_list *));
 381                for (i = 0; i < len; i++) {
 382                        posns[i] = create_tree_entry_list((struct tree *) posn->item);
 383                        posn = posn->next;
 384                }
 385                if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "",
 386                                     o, &indpos, &df_conflict_list))
 387                        return -1;
 388        }
 389
 390        if (o->trivial_merges_only && o->nontrivial_merge)
 391                die("Merge requires file-level merging");
 392
 393        check_updates(active_cache, active_nr, o);
 394        return 0;
 395}