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