rev-parse.con commit git-rev-parse: Allow a "zeroth" parent of a commit - the commit itself. (79162bb)
   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        if (!idx) {
 101                memcpy(result, commit->object.sha1, 20);
 102                return 0;
 103        }
 104        p = commit->parents;
 105        while (p) {
 106                if (!--idx) {
 107                        memcpy(result, p->item->object.sha1, 20);
 108                        return 0;
 109                }
 110                p = p->next;
 111        }
 112        return -1;
 113}
 114
 115static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
 116{
 117        static char dirname[PATH_MAX];
 118        char hex[40];
 119        DIR *dir;
 120        int found;
 121
 122        snprintf(dirname, sizeof(dirname), "%s/%.2s", get_object_directory(), name);
 123        dir = opendir(dirname);
 124        sprintf(hex, "%.2s", name);
 125        found = 0;
 126        if (dir) {
 127                struct dirent *de;
 128                while ((de = readdir(dir)) != NULL) {
 129                        if (strlen(de->d_name) != 38)
 130                                continue;
 131                        if (memcmp(de->d_name, name + 2, len-2))
 132                                continue;
 133                        memcpy(hex + 2, de->d_name, 38);
 134                        if (++found > 1)
 135                                break;
 136                }
 137                closedir(dir);
 138        }
 139        if (found == 1)
 140                return get_sha1_hex(hex, sha1) == 0;
 141        return 0;
 142}
 143
 144static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b)
 145{
 146        do {
 147                if (*a != *b)
 148                        return 0;
 149                a++;
 150                b++;
 151                len -= 2;
 152        } while (len > 1);
 153        if (len)
 154                if ((*a ^ *b) & 0xf0)
 155                        return 0;
 156        return 1;
 157}
 158
 159static int find_short_packed_object(int len, const unsigned char *match, unsigned char *sha1)
 160{
 161        struct packed_git *p;
 162
 163        prepare_packed_git();
 164        for (p = packed_git; p; p = p->next) {
 165                unsigned num = num_packed_objects(p);
 166                unsigned first = 0, last = num;
 167                while (first < last) {
 168                        unsigned mid = (first + last) / 2;
 169                        unsigned char now[20];
 170                        int cmp;
 171
 172                        nth_packed_object_sha1(p, mid, now);
 173                        cmp = memcmp(match, now, 20);
 174                        if (!cmp) {
 175                                first = mid;
 176                                break;
 177                        }
 178                        if (cmp > 0) {
 179                                first = mid+1;
 180                                continue;
 181                        }
 182                        last = mid;
 183                }
 184                if (first < num) {
 185                        unsigned char now[20], next[20];
 186                        nth_packed_object_sha1(p, first, now);
 187                        if (match_sha(len, match, now)) {
 188                                if (nth_packed_object_sha1(p, first+1, next) || !match_sha(len, match, next)) {
 189                                        memcpy(sha1, now, 20);
 190                                        return 1;
 191                                }
 192                        }
 193                }       
 194        }
 195        return 0;
 196}
 197
 198static int get_short_sha1(char *name, unsigned char *sha1)
 199{
 200        int i;
 201        char canonical[40];
 202        unsigned char res[20];
 203
 204        memset(res, 0, 20);
 205        memset(canonical, 'x', 40);
 206        for (i = 0;;i++) {
 207                unsigned char c = name[i];
 208                unsigned char val;
 209                if (!c || i > 40)
 210                        break;
 211                if (c >= '0' && c <= '9')
 212                        val = c - '0';
 213                else if (c >= 'a' && c <= 'f')
 214                        val = c - 'a' + 10;
 215                else if (c >= 'A' && c <='F') {
 216                        val = c - 'A' + 10;
 217                        c -= 'A' - 'a';
 218                }
 219                else
 220                        return -1;
 221                canonical[i] = c;
 222                if (!(i & 1))
 223                        val <<= 4;
 224                res[i >> 1] |= val;
 225        }
 226        if (i < 4)
 227                return -1;
 228        if (find_short_object_filename(i, canonical, sha1))
 229                return 0;
 230        if (find_short_packed_object(i, res, sha1))
 231                return 0;
 232        return -1;
 233}
 234
 235/*
 236 * This is like "get_sha1()", except it allows "sha1 expressions",
 237 * notably "xyz^" for "parent of xyz"
 238 */
 239static int get_extended_sha1(char *name, unsigned char *sha1)
 240{
 241        int parent, ret;
 242        int len = strlen(name);
 243
 244        parent = 1;
 245        if (len > 2 && name[len-1] >= '0' && name[len-1] <= '9') {
 246                parent = name[len-1] - '0';
 247                len--;
 248        }
 249        if (len > 1 && name[len-1] == '^') {
 250                name[len-1] = 0;
 251                ret = get_parent(name, sha1, parent);
 252                name[len-1] = '^';
 253                if (!ret)
 254                        return 0;
 255        }
 256        ret = get_sha1(name, sha1);
 257        if (!ret)
 258                return 0;
 259        return get_short_sha1(name, sha1);
 260}
 261
 262static void show_default(void)
 263{
 264        char *s = def;
 265
 266        if (s) {
 267                unsigned char sha1[20];
 268
 269                def = NULL;
 270                if (!get_extended_sha1(s, sha1)) {
 271                        show_rev(NORMAL, sha1);
 272                        return;
 273                }
 274                show_arg(s);
 275        }
 276}
 277
 278static int show_reference(const char *refname, const unsigned char *sha1)
 279{
 280        show_rev(NORMAL, sha1);
 281        return 0;
 282}
 283
 284int main(int argc, char **argv)
 285{
 286        int i, as_is = 0;
 287        unsigned char sha1[20];
 288
 289        for (i = 1; i < argc; i++) {
 290                char *arg = argv[i];
 291                char *dotdot;
 292        
 293                if (as_is) {
 294                        show_norev(arg);
 295                        continue;
 296                }
 297                if (*arg == '-') {
 298                        if (!strcmp(arg, "--")) {
 299                                show_default();
 300                                if (revs_only)
 301                                        break;
 302                                as_is = 1;
 303                        }
 304                        if (!strcmp(arg, "--default")) {
 305                                def = argv[i+1];
 306                                i++;
 307                                continue;
 308                        }
 309                        if (!strcmp(arg, "--revs-only")) {
 310                                revs_only = 1;
 311                                continue;
 312                        }
 313                        if (!strcmp(arg, "--no-revs")) {
 314                                no_revs = 1;
 315                                continue;
 316                        }
 317                        if (!strcmp(arg, "--flags")) {
 318                                flags_only = 1;
 319                                continue;
 320                        }
 321                        if (!strcmp(arg, "--no-flags")) {
 322                                no_flags = 1;
 323                                continue;
 324                        }
 325                        if (!strcmp(arg, "--verify")) {
 326                                revs_only = 1;
 327                                do_rev_argument = 0;
 328                                single_rev = 1;
 329                                continue;
 330                        }
 331                        if (!strcmp(arg, "--not")) {
 332                                show_type ^= REVERSED;
 333                                continue;
 334                        }
 335                        if (!strcmp(arg, "--all")) {
 336                                for_each_ref(show_reference);
 337                                continue;
 338                        }
 339                        show_arg(arg);
 340                        continue;
 341                }
 342                dotdot = strstr(arg, "..");
 343                if (dotdot) {
 344                        unsigned char end[20];
 345                        char *n = dotdot+2;
 346                        *dotdot = 0;
 347                        if (!get_extended_sha1(arg, sha1)) {
 348                                if (!*n)
 349                                        n = "HEAD";
 350                                if (!get_extended_sha1(n, end)) {
 351                                        if (no_revs)
 352                                                continue;
 353                                        def = NULL;
 354                                        show_rev(NORMAL, end);
 355                                        show_rev(REVERSED, sha1);
 356                                        continue;
 357                                }
 358                        }
 359                        *dotdot = '.';
 360                }
 361                if (!get_extended_sha1(arg, sha1)) {
 362                        if (no_revs)
 363                                continue;
 364                        def = NULL;
 365                        show_rev(NORMAL, sha1);
 366                        continue;
 367                }
 368                if (*arg == '^' && !get_extended_sha1(arg+1, sha1)) {
 369                        if (no_revs)
 370                                continue;
 371                        def = NULL;
 372                        show_rev(REVERSED, sha1);
 373                        continue;
 374                }
 375                show_default();
 376                show_norev(arg);
 377        }
 378        show_default();
 379        if (single_rev && output_revs != 1) {
 380                fprintf(stderr, "Needed a single revision\n");
 381                exit(1);
 382        }
 383        return 0;
 384}