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