rev-parse.con commit Make "upload-pack" match git-fetch-pack usage (113b947)
   1/*
   2 * rev-parse.c
   3 *
   4 * Copyright (C) Linus Torvalds, 2005
   5 */
   6#include "cache.h"
   7#include "commit.h"
   8#include "refs.h"
   9
  10static char *def = NULL;
  11static int no_revs = 0;
  12static int single_rev = 0;
  13static int revs_only = 0;
  14static int do_rev_argument = 1;
  15static int output_revs = 0;
  16static int flags_only = 0;
  17static int no_flags = 0;
  18
  19#define NORMAL 0
  20#define REVERSED 1
  21static int show_type = NORMAL;
  22
  23static int get_extended_sha1(char *name, unsigned char *sha1);
  24
  25/*
  26 * Some arguments are relevant "revision" arguments,
  27 * others are about output format or other details.
  28 * This sorts it all out.
  29 */
  30static int is_rev_argument(const char *arg)
  31{
  32        static const char *rev_args[] = {
  33                "--max-count=",
  34                "--max-age=",
  35                "--min-age=",
  36                "--merge-order",
  37                NULL
  38        };
  39        const char **p = rev_args;
  40
  41        for (;;) {
  42                const char *str = *p++;
  43                int len;
  44                if (!str)
  45                        return 0;
  46                len = strlen(str);
  47                if (!strncmp(arg, str, len))
  48                        return 1;
  49        }
  50}
  51
  52static void show_rev(int type, const unsigned char *sha1)
  53{
  54        if (no_revs)
  55                return;
  56        output_revs++;
  57        printf("%s%s\n", type == show_type ? "" : "^", sha1_to_hex(sha1));
  58}
  59
  60static void show_rev_arg(char *rev)
  61{
  62        if (no_revs)
  63                return;
  64        puts(rev);
  65}
  66
  67static void show_norev(char *norev)
  68{
  69        if (flags_only)
  70                return;
  71        if (revs_only)
  72                return;
  73        puts(norev);
  74}
  75
  76static void show_arg(char *arg)
  77{
  78        if (no_flags)
  79                return;
  80        if (do_rev_argument && is_rev_argument(arg))
  81                show_rev_arg(arg);
  82        else
  83                show_norev(arg);
  84}
  85
  86static int get_parent(char *name, unsigned char *result, int idx)
  87{
  88        unsigned char sha1[20];
  89        int ret = get_extended_sha1(name, sha1);
  90        struct commit *commit;
  91        struct commit_list *p;
  92
  93        if (ret)
  94                return ret;
  95        commit = lookup_commit_reference(sha1);
  96        if (!commit)
  97                return -1;
  98        if (parse_commit(commit))
  99                return -1;
 100        p = commit->parents;
 101        while (p) {
 102                if (!--idx) {
 103                        memcpy(result, p->item->object.sha1, 20);
 104                        return 0;
 105                }
 106                p = p->next;
 107        }
 108        return -1;
 109}
 110
 111static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
 112{
 113        static char dirname[PATH_MAX];
 114        char hex[40];
 115        DIR *dir;
 116        int found;
 117
 118        snprintf(dirname, sizeof(dirname), "%s/%.2s", get_object_directory(), name);
 119        dir = opendir(dirname);
 120        sprintf(hex, "%.2s", name);
 121        found = 0;
 122        if (dir) {
 123                struct dirent *de;
 124                while ((de = readdir(dir)) != NULL) {
 125                        if (strlen(de->d_name) != 38)
 126                                continue;
 127                        if (memcmp(de->d_name, name + 2, len-2))
 128                                continue;
 129                        memcpy(hex + 2, de->d_name, 38);
 130                        if (++found > 1)
 131                                break;
 132                }
 133                closedir(dir);
 134        }
 135        if (found == 1)
 136                return get_sha1_hex(hex, sha1) == 0;
 137        return 0;
 138}
 139
 140static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b)
 141{
 142        do {
 143                if (*a != *b)
 144                        return 0;
 145                a++;
 146                b++;
 147                len -= 2;
 148        } while (len > 1);
 149        if (len)
 150                if ((*a ^ *b) & 0xf0)
 151                        return 0;
 152        return 1;
 153}
 154
 155static int find_short_packed_object(int len, const unsigned char *match, unsigned char *sha1)
 156{
 157        struct packed_git *p;
 158
 159        prepare_packed_git();
 160        for (p = packed_git; p; p = p->next) {
 161                unsigned num = num_packed_objects(p);
 162                unsigned first = 0, last = num;
 163                while (first < last) {
 164                        unsigned mid = (first + last) / 2;
 165                        unsigned char now[20];
 166                        int cmp;
 167
 168                        nth_packed_object_sha1(p, mid, now);
 169                        cmp = memcmp(match, now, 20);
 170                        if (!cmp) {
 171                                first = mid;
 172                                break;
 173                        }
 174                        if (cmp > 0) {
 175                                first = mid+1;
 176                                continue;
 177                        }
 178                        last = mid;
 179                }
 180                if (first < num) {
 181                        unsigned char now[20], next[20];
 182                        nth_packed_object_sha1(p, first, now);
 183                        if (match_sha(len, match, now)) {
 184                                if (nth_packed_object_sha1(p, first+1, next) || !match_sha(len, match, next)) {
 185                                        memcpy(sha1, now, 20);
 186                                        return 1;
 187                                }
 188                        }
 189                }       
 190        }
 191        return 0;
 192}
 193
 194static int get_short_sha1(char *name, unsigned char *sha1)
 195{
 196        int i;
 197        char canonical[40];
 198        unsigned char res[20];
 199
 200        memset(res, 0, 20);
 201        memset(canonical, 'x', 40);
 202        for (i = 0;;i++) {
 203                unsigned char c = name[i];
 204                unsigned char val;
 205                if (!c || i > 40)
 206                        break;
 207                if (c >= '0' && c <= '9')
 208                        val = c - '0';
 209                else if (c >= 'a' && c <= 'f')
 210                        val = c - 'a' + 10;
 211                else if (c >= 'A' && c <='F') {
 212                        val = c - 'A' + 10;
 213                        c -= 'A' - 'a';
 214                }
 215                else
 216                        return -1;
 217                canonical[i] = c;
 218                if (!(i & 1))
 219                        val <<= 4;
 220                res[i >> 1] |= val;
 221        }
 222        if (i < 4)
 223                return -1;
 224        if (find_short_object_filename(i, canonical, sha1))
 225                return 0;
 226        if (find_short_packed_object(i, res, sha1))
 227                return 0;
 228        return -1;
 229}
 230
 231/*
 232 * This is like "get_sha1()", except it allows "sha1 expressions",
 233 * notably "xyz^" for "parent of xyz"
 234 */
 235static int get_extended_sha1(char *name, unsigned char *sha1)
 236{
 237        int parent, ret;
 238        int len = strlen(name);
 239
 240        parent = 1;
 241        if (len > 2 && name[len-1] >= '1' && name[len-1] <= '9') {
 242                parent = name[len-1] - '0';
 243                len--;
 244        }
 245        if (len > 1 && name[len-1] == '^') {
 246                name[len-1] = 0;
 247                ret = get_parent(name, sha1, parent);
 248                name[len-1] = '^';
 249                if (!ret)
 250                        return 0;
 251        }
 252        ret = get_sha1(name, sha1);
 253        if (!ret)
 254                return 0;
 255        return get_short_sha1(name, sha1);
 256}
 257
 258static void show_default(void)
 259{
 260        char *s = def;
 261
 262        if (s) {
 263                unsigned char sha1[20];
 264
 265                def = NULL;
 266                if (!get_extended_sha1(s, sha1)) {
 267                        show_rev(NORMAL, sha1);
 268                        return;
 269                }
 270                show_arg(s);
 271        }
 272}
 273
 274static int show_reference(const char *refname, const unsigned char *sha1)
 275{
 276        show_rev(NORMAL, sha1);
 277        return 0;
 278}
 279
 280int main(int argc, char **argv)
 281{
 282        int i, as_is = 0;
 283        unsigned char sha1[20];
 284
 285        for (i = 1; i < argc; i++) {
 286                char *arg = argv[i];
 287                char *dotdot;
 288        
 289                if (as_is) {
 290                        show_norev(arg);
 291                        continue;
 292                }
 293                if (*arg == '-') {
 294                        if (!strcmp(arg, "--")) {
 295                                show_default();
 296                                if (revs_only)
 297                                        break;
 298                                as_is = 1;
 299                        }
 300                        if (!strcmp(arg, "--default")) {
 301                                def = argv[i+1];
 302                                i++;
 303                                continue;
 304                        }
 305                        if (!strcmp(arg, "--revs-only")) {
 306                                revs_only = 1;
 307                                continue;
 308                        }
 309                        if (!strcmp(arg, "--no-revs")) {
 310                                no_revs = 1;
 311                                continue;
 312                        }
 313                        if (!strcmp(arg, "--flags")) {
 314                                flags_only = 1;
 315                                continue;
 316                        }
 317                        if (!strcmp(arg, "--no-flags")) {
 318                                no_flags = 1;
 319                                continue;
 320                        }
 321                        if (!strcmp(arg, "--verify")) {
 322                                revs_only = 1;
 323                                do_rev_argument = 0;
 324                                single_rev = 1;
 325                                continue;
 326                        }
 327                        if (!strcmp(arg, "--not")) {
 328                                show_type ^= REVERSED;
 329                                continue;
 330                        }
 331                        if (!strcmp(arg, "--all")) {
 332                                for_each_ref(show_reference);
 333                                continue;
 334                        }
 335                        show_arg(arg);
 336                        continue;
 337                }
 338                dotdot = strstr(arg, "..");
 339                if (dotdot) {
 340                        unsigned char end[20];
 341                        char *n = dotdot+2;
 342                        *dotdot = 0;
 343                        if (!get_extended_sha1(arg, sha1)) {
 344                                if (!*n)
 345                                        n = "HEAD";
 346                                if (!get_extended_sha1(n, end)) {
 347                                        if (no_revs)
 348                                                continue;
 349                                        def = NULL;
 350                                        show_rev(NORMAL, end);
 351                                        show_rev(REVERSED, sha1);
 352                                        continue;
 353                                }
 354                        }
 355                        *dotdot = '.';
 356                }
 357                if (!get_extended_sha1(arg, sha1)) {
 358                        if (no_revs)
 359                                continue;
 360                        def = NULL;
 361                        show_rev(NORMAL, sha1);
 362                        continue;
 363                }
 364                if (*arg == '^' && !get_extended_sha1(arg+1, sha1)) {
 365                        if (no_revs)
 366                                continue;
 367                        def = NULL;
 368                        show_rev(REVERSED, sha1);
 369                        continue;
 370                }
 371                show_default();
 372                show_norev(arg);
 373        }
 374        show_default();
 375        if (single_rev && output_revs != 1) {
 376                fprintf(stderr, "Needed a single revision\n");
 377                exit(1);
 378        }
 379        return 0;
 380}