Merge branch 'jc/maint-refs-dangling' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 31 Mar 2010 22:09:32 +0000 (15:09 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 31 Mar 2010 22:09:32 +0000 (15:09 -0700)
* jc/maint-refs-dangling:
refs: ref entry with NULL sha1 is can be a dangling symref

1  2 
refs.c
t/t5505-remote.sh
diff --combined refs.c
index 63e30d74a7eacee53682b20068e8e24a37840ccc,9c12f5ea8c92452d892f5b7ce70757e2114d338f..a7518b6f0ddb505aa0133d5920a76a19f8e00a75
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -6,6 -6,7 +6,7 @@@
  
  /* ISSYMREF=01 and ISPACKED=02 are public interfaces */
  #define REF_KNOWS_PEELED 04
+ #define REF_BROKEN 010
  
  struct ref_list {
        struct ref_list *next;
@@@ -275,8 -276,10 +276,10 @@@ static struct ref_list *get_ref_dir(con
                                list = get_ref_dir(ref, list);
                                continue;
                        }
-                       if (!resolve_ref(ref, sha1, 1, &flag))
+                       if (!resolve_ref(ref, sha1, 1, &flag)) {
                                hashclr(sha1);
+                               flag |= REF_BROKEN;
+                       }
                        list = add_ref(ref, sha1, flag, list, NULL);
                }
                free(ref);
  }
  
  struct warn_if_dangling_data {
 +      FILE *fp;
        const char *refname;
        const char *msg_fmt;
  };
@@@ -305,13 -307,13 +308,13 @@@ static int warn_if_dangling_symref(cons
        if (!resolves_to || strcmp(resolves_to, d->refname))
                return 0;
  
 -      printf(d->msg_fmt, refname);
 +      fprintf(d->fp, d->msg_fmt, refname);
        return 0;
  }
  
 -void warn_dangling_symref(const char *msg_fmt, const char *refname)
 +void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
  {
 -      struct warn_if_dangling_data data = { refname, msg_fmt };
 +      struct warn_if_dangling_data data = { fp, refname, msg_fmt };
        for_each_rawref(warn_if_dangling_symref, &data);
  }
  
@@@ -519,13 -521,6 +522,13 @@@ const char *resolve_ref(const char *ref
        return ref;
  }
  
 +/* The argument to filter_refs */
 +struct ref_filter {
 +      const char *pattern;
 +      each_ref_fn *fn;
 +      void *cb_data;
 +};
 +
  int read_ref(const char *ref, unsigned char *sha1)
  {
        if (resolve_ref(ref, sha1, 1, NULL))
@@@ -539,10 -534,10 +542,10 @@@ static int do_one_ref(const char *base
  {
        if (strncmp(base, entry->name, trim))
                return 0;
-       /* Is this a "negative ref" that represents a deleted ref? */
-       if (is_null_sha1(entry->sha1))
-               return 0;
        if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
+               if (entry->flag & REF_BROKEN)
+                       return 0; /* ignore dangling symref */
                if (!has_sha1_file(entry->sha1)) {
                        error("%s does not point to a valid object!", entry->name);
                        return 0;
        return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
  }
  
 +static int filter_refs(const char *ref, const unsigned char *sha, int flags,
 +      void *data)
 +{
 +      struct ref_filter *filter = (struct ref_filter *)data;
 +      if (fnmatch(filter->pattern, ref, 0))
 +              return 0;
 +      return filter->fn(ref, sha, flags, filter->cb_data);
 +}
 +
  int peel_ref(const char *ref, unsigned char *sha1)
  {
        int flag;
@@@ -685,48 -671,6 +688,48 @@@ int for_each_remote_ref(each_ref_fn fn
        return for_each_ref_in("refs/remotes/", fn, cb_data);
  }
  
 +int for_each_replace_ref(each_ref_fn fn, void *cb_data)
 +{
 +      return do_for_each_ref("refs/replace/", fn, 13, 0, cb_data);
 +}
 +
 +int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
 +      const char *prefix, void *cb_data)
 +{
 +      struct strbuf real_pattern = STRBUF_INIT;
 +      struct ref_filter filter;
 +      const char *has_glob_specials;
 +      int ret;
 +
 +      if (!prefix && prefixcmp(pattern, "refs/"))
 +              strbuf_addstr(&real_pattern, "refs/");
 +      else if (prefix)
 +              strbuf_addstr(&real_pattern, prefix);
 +      strbuf_addstr(&real_pattern, pattern);
 +
 +      has_glob_specials = strpbrk(pattern, "?*[");
 +      if (!has_glob_specials) {
 +              /* Append implied '/' '*' if not present. */
 +              if (real_pattern.buf[real_pattern.len - 1] != '/')
 +                      strbuf_addch(&real_pattern, '/');
 +              /* No need to check for '*', there is none. */
 +              strbuf_addch(&real_pattern, '*');
 +      }
 +
 +      filter.pattern = real_pattern.buf;
 +      filter.fn = fn;
 +      filter.cb_data = cb_data;
 +      ret = for_each_ref(filter_refs, &filter);
 +
 +      strbuf_release(&real_pattern);
 +      return ret;
 +}
 +
 +int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
 +{
 +      return for_each_glob_ref_in(fn, pattern, NULL, cb_data);
 +}
 +
  int for_each_rawref(each_ref_fn fn, void *cb_data)
  {
        return do_for_each_ref("refs/", fn, 0,
   * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
   * - it ends with a "/".
   * - it ends with ".lock"
 + * - it contains a "\" (backslash)
   */
  
  static inline int bad_ref_char(int ch)
  {
        if (((unsigned) ch) <= ' ' ||
 -          ch == '~' || ch == '^' || ch == ':')
 +          ch == '~' || ch == '^' || ch == ':' || ch == '\\')
                return 1;
        /* 2.13 Pattern Matching Notation */
        if (ch == '?' || ch == '[') /* Unsupported */
@@@ -811,8 -754,9 +814,8 @@@ int check_ref_format(const char *ref
        }
  }
  
 -const char *prettify_ref(const struct ref *ref)
 +const char *prettify_refname(const char *name)
  {
 -      const char *name = ref->name;
        return name + (
                !prefixcmp(name, "refs/heads/") ? 11 :
                !prefixcmp(name, "refs/tags/") ? 10 :
@@@ -880,7 -824,7 +883,7 @@@ static int remove_empty_directories(con
        strbuf_init(&path, 20);
        strbuf_addstr(&path, file);
  
 -      result = remove_dir_recursively(&path, 1);
 +      result = remove_dir_recursively(&path, REMOVE_DIR_EMPTY_ONLY);
  
        strbuf_release(&path);
  
@@@ -1026,10 -970,8 +1029,10 @@@ static int repack_without_ref(const cha
        if (!found)
                return 0;
        fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
 -      if (fd < 0)
 +      if (fd < 0) {
 +              unable_to_lock_error(git_path("packed-refs"), errno);
                return error("cannot delete '%s' from packed refs", refname);
 +      }
  
        for (list = packed_ref_list; list; list = list->next) {
                char line[PATH_MAX + 100];
@@@ -1480,7 -1422,7 +1483,7 @@@ int read_ref_at(const char *ref, unsign
        logfile = git_path("logs/%s", ref);
        logfd = open(logfile, O_RDONLY, 0);
        if (logfd < 0)
 -              die("Unable to read log %s: %s", logfile, strerror(errno));
 +              die_errno("Unable to read log '%s'", logfile);
        fstat(logfd, &st);
        if (!st.st_size)
                die("Log %s is empty.", logfile);
@@@ -1574,7 -1516,7 +1577,7 @@@ int for_each_recent_reflog_ent(const ch
  {
        const char *logfile;
        FILE *logfp;
 -      char buf[1024];
 +      struct strbuf sb = STRBUF_INIT;
        int ret = 0;
  
        logfile = git_path("logs/%s", ref);
                if (fstat(fileno(logfp), &statbuf) ||
                    statbuf.st_size < ofs ||
                    fseek(logfp, -ofs, SEEK_END) ||
 -                  fgets(buf, sizeof(buf), logfp)) {
 +                  strbuf_getwholeline(&sb, logfp, '\n')) {
                        fclose(logfp);
 +                      strbuf_release(&sb);
                        return -1;
                }
        }
  
 -      while (fgets(buf, sizeof(buf), logfp)) {
 +      while (!strbuf_getwholeline(&sb, logfp, '\n')) {
                unsigned char osha1[20], nsha1[20];
                char *email_end, *message;
                unsigned long timestamp;
 -              int len, tz;
 +              int tz;
  
                /* old SP new SP name <email> SP time TAB msg LF */
 -              len = strlen(buf);
 -              if (len < 83 || buf[len-1] != '\n' ||
 -                  get_sha1_hex(buf, osha1) || buf[40] != ' ' ||
 -                  get_sha1_hex(buf + 41, nsha1) || buf[81] != ' ' ||
 -                  !(email_end = strchr(buf + 82, '>')) ||
 +              if (sb.len < 83 || sb.buf[sb.len - 1] != '\n' ||
 +                  get_sha1_hex(sb.buf, osha1) || sb.buf[40] != ' ' ||
 +                  get_sha1_hex(sb.buf + 41, nsha1) || sb.buf[81] != ' ' ||
 +                  !(email_end = strchr(sb.buf + 82, '>')) ||
                    email_end[1] != ' ' ||
                    !(timestamp = strtoul(email_end + 2, &message, 10)) ||
                    !message || message[0] != ' ' ||
                        message += 6;
                else
                        message += 7;
 -              ret = fn(osha1, nsha1, buf+82, timestamp, tz, message, cb_data);
 +              ret = fn(osha1, nsha1, sb.buf + 82, timestamp, tz, message,
 +                       cb_data);
                if (ret)
                        break;
        }
        fclose(logfp);
 +      strbuf_release(&sb);
        return ret;
  }
  
diff --combined t/t5505-remote.sh
index a82c5ffa1c608f45786fe37531ffe93008a3570b,98e8ec03d3f1abb22d58bdf8adbb952414f0c59b..26920502093a98d4b5721f783e006de9d999792a
@@@ -135,8 -135,7 +135,8 @@@ EO
  
  cat > test/expect << EOF
  * remote origin
 -  URL: $(pwd)/one
 +  Fetch URL: $(pwd)/one
 +  Push  URL: $(pwd)/one
    HEAD branch: master
    Remote branches:
      master new (next fetch will store in remotes/origin)
      master pushes to master   (local out of date)
      master pushes to upstream (create)
  * remote two
 -  URL: ../two
 +  Fetch URL: ../two
 +  Push  URL: ../three
    HEAD branch (remote HEAD is ambiguous, may be one of the following):
      another
      master
    Local refs configured for 'git push':
 -    ahead  forces to master  (fast forwardable)
 +    ahead  forces to master  (fast-forwardable)
      master pushes to another (up to date)
  EOF
  
@@@ -175,7 -173,6 +175,7 @@@ test_expect_success 'show' 
         git branch --track rebase origin/master &&
         git branch -d -r origin/master &&
         git config --add remote.two.url ../two &&
 +       git config --add remote.two.pushurl ../three &&
         git config branch.rebase.rebase true &&
         git config branch.octopus.merge "topic-a topic-b topic-c" &&
         (cd ../one &&
  
  cat > test/expect << EOF
  * remote origin
 -  URL: $(pwd)/one
 +  Fetch URL: $(pwd)/one
 +  Push  URL: $(pwd)/one
    HEAD branch: (not queried)
    Remote branches: (status not queried)
      master
@@@ -365,17 -361,6 +365,17 @@@ test_expect_success 'update with argume
  
  '
  
 +test_expect_success 'update --prune' '
 +
 +      (cd one &&
 +       git branch -m side2 side3) &&
 +      (cd test &&
 +       git remote update --prune &&
 +       (cd ../one && git branch -m side3 side2)
 +       git rev-parse refs/remotes/origin/side3 &&
 +       test_must_fail git rev-parse refs/remotes/origin/side2)
 +'
 +
  cat > one/expect << EOF
    apis/master
    apis/side
@@@ -419,20 -404,6 +419,20 @@@ test_expect_success 'update default (ov
  
  '
  
 +test_expect_success 'update (with remotes.default defined)' '
 +
 +      (cd one &&
 +       for b in $(git branch -r)
 +       do
 +              git branch -r -d $b || break
 +       done &&
 +       git config remotes.default "drosophila" &&
 +       git remote update &&
 +       git branch -r > output &&
 +       test_cmp expect output)
 +
 +'
 +
  test_expect_success '"remote show" does not show symbolic refs' '
  
        git clone one three &&
@@@ -507,15 -478,15 +507,15 @@@ test_expect_success 'remote prune to ca
        (
                cd seven &&
                git remote prune origin
-       ) 2>err &&
+       ) >err 2>&1 &&
        grep "has become dangling" err &&
  
-       : And the dangling symref will not cause other annoying errors
+       : And the dangling symref will not cause other annoying errors &&
        (
                cd seven &&
                git branch -a
        ) 2>err &&
-       ! grep "points nowhere" err
+       ! grep "points nowhere" err &&
        (
                cd seven &&
                test_must_fail git branch nomore origin
@@@ -533,219 -504,5 +533,219 @@@ test_expect_success 'show empty remote
        )
  '
  
 -test_done
 +test_expect_success 'new remote' '
 +(
 +      git remote add someremote foo &&
 +      echo foo >expect &&
 +      git config --get-all remote.someremote.url >actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_expect_success 'remote set-url bar' '
 +(
 +      git remote set-url someremote bar &&
 +      echo bar >expect &&
 +      git config --get-all remote.someremote.url >actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_expect_success 'remote set-url baz bar' '
 +(
 +      git remote set-url someremote baz bar &&
 +      echo baz >expect &&
 +      git config --get-all remote.someremote.url >actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_expect_success 'remote set-url zot bar' '
 +(
 +      test_must_fail git remote set-url someremote zot bar &&
 +      echo baz >expect &&
 +      git config --get-all remote.someremote.url >actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_expect_success 'remote set-url --push zot baz' '
 +(
 +      test_must_fail git remote set-url --push someremote zot baz &&
 +      echo "YYY" >expect &&
 +      echo baz >>expect &&
 +      test_must_fail git config --get-all remote.someremote.pushurl >actual &&
 +      echo "YYY" >>actual &&
 +      git config --get-all remote.someremote.url >>actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_expect_success 'remote set-url --push zot' '
 +(
 +      git remote set-url --push someremote zot &&
 +      echo zot >expect &&
 +      echo "YYY" >>expect &&
 +      echo baz >>expect &&
 +      git config --get-all remote.someremote.pushurl >actual &&
 +      echo "YYY" >>actual &&
 +      git config --get-all remote.someremote.url >>actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_expect_success 'remote set-url --push qux zot' '
 +(
 +      git remote set-url --push someremote qux zot &&
 +      echo qux >expect &&
 +      echo "YYY" >>expect &&
 +      echo baz >>expect &&
 +      git config --get-all remote.someremote.pushurl >actual &&
 +      echo "YYY" >>actual &&
 +      git config --get-all remote.someremote.url >>actual &&
 +      cmp expect actual
 +)
 +'
  
 +test_expect_success 'remote set-url --push foo qu+x' '
 +(
 +      git remote set-url --push someremote foo qu+x &&
 +      echo foo >expect &&
 +      echo "YYY" >>expect &&
 +      echo baz >>expect &&
 +      git config --get-all remote.someremote.pushurl >actual &&
 +      echo "YYY" >>actual &&
 +      git config --get-all remote.someremote.url >>actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_expect_success 'remote set-url --push --add aaa' '
 +(
 +      git remote set-url --push --add someremote aaa &&
 +      echo foo >expect &&
 +      echo aaa >>expect &&
 +      echo "YYY" >>expect &&
 +      echo baz >>expect &&
 +      git config --get-all remote.someremote.pushurl >actual &&
 +      echo "YYY" >>actual &&
 +      git config --get-all remote.someremote.url >>actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_expect_success 'remote set-url --push bar aaa' '
 +(
 +      git remote set-url --push someremote bar aaa &&
 +      echo foo >expect &&
 +      echo bar >>expect &&
 +      echo "YYY" >>expect &&
 +      echo baz >>expect &&
 +      git config --get-all remote.someremote.pushurl >actual &&
 +      echo "YYY" >>actual &&
 +      git config --get-all remote.someremote.url >>actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_expect_success 'remote set-url --push --delete bar' '
 +(
 +      git remote set-url --push --delete someremote bar &&
 +      echo foo >expect &&
 +      echo "YYY" >>expect &&
 +      echo baz >>expect &&
 +      git config --get-all remote.someremote.pushurl >actual &&
 +      echo "YYY" >>actual &&
 +      git config --get-all remote.someremote.url >>actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_expect_success 'remote set-url --push --delete foo' '
 +(
 +      git remote set-url --push --delete someremote foo &&
 +      echo "YYY" >expect &&
 +      echo baz >>expect &&
 +      test_must_fail git config --get-all remote.someremote.pushurl >actual &&
 +      echo "YYY" >>actual &&
 +      git config --get-all remote.someremote.url >>actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_expect_success 'remote set-url --add bbb' '
 +(
 +      git remote set-url --add someremote bbb &&
 +      echo "YYY" >expect &&
 +      echo baz >>expect &&
 +      echo bbb >>expect &&
 +      test_must_fail git config --get-all remote.someremote.pushurl >actual &&
 +      echo "YYY" >>actual &&
 +      git config --get-all remote.someremote.url >>actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_expect_success 'remote set-url --delete .*' '
 +(
 +      test_must_fail git remote set-url --delete someremote .* &&
 +      echo "YYY" >expect &&
 +      echo baz >>expect &&
 +      echo bbb >>expect &&
 +      test_must_fail git config --get-all remote.someremote.pushurl >actual &&
 +      echo "YYY" >>actual &&
 +      git config --get-all remote.someremote.url >>actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_expect_success 'remote set-url --delete bbb' '
 +(
 +      git remote set-url --delete someremote bbb &&
 +      echo "YYY" >expect &&
 +      echo baz >>expect &&
 +      test_must_fail git config --get-all remote.someremote.pushurl >actual &&
 +      echo "YYY" >>actual &&
 +      git config --get-all remote.someremote.url >>actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_expect_success 'remote set-url --delete baz' '
 +(
 +      test_must_fail git remote set-url --delete someremote baz &&
 +      echo "YYY" >expect &&
 +      echo baz >>expect &&
 +      test_must_fail git config --get-all remote.someremote.pushurl >actual &&
 +      echo "YYY" >>actual &&
 +      git config --get-all remote.someremote.url >>actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_expect_success 'remote set-url --add ccc' '
 +(
 +      git remote set-url --add someremote ccc &&
 +      echo "YYY" >expect &&
 +      echo baz >>expect &&
 +      echo ccc >>expect &&
 +      test_must_fail git config --get-all remote.someremote.pushurl >actual &&
 +      echo "YYY" >>actual &&
 +      git config --get-all remote.someremote.url >>actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_expect_success 'remote set-url --delete baz' '
 +(
 +      git remote set-url --delete someremote baz &&
 +      echo "YYY" >expect &&
 +      echo ccc >>expect &&
 +      test_must_fail git config --get-all remote.someremote.pushurl >actual &&
 +      echo "YYY" >>actual &&
 +      git config --get-all remote.someremote.url >>actual &&
 +      cmp expect actual
 +)
 +'
 +
 +test_done