|
5 | 5 | #include "cache.h" |
6 | 6 | #include "hashmap.h" |
7 | 7 | #include "lockfile.h" |
| 8 | +#include "iterator.h" |
8 | 9 | #include "refs.h" |
9 | 10 | #include "refs/refs-internal.h" |
10 | 11 | #include "object.h" |
@@ -1658,11 +1659,91 @@ int ref_transaction_commit(struct ref_transaction *transaction, |
1658 | 1659 |
|
1659 | 1660 | int refs_verify_refname_available(struct ref_store *refs, |
1660 | 1661 | const char *refname, |
1661 | | - const struct string_list *extra, |
| 1662 | + const struct string_list *extras, |
1662 | 1663 | const struct string_list *skip, |
1663 | 1664 | struct strbuf *err) |
1664 | 1665 | { |
1665 | | - return refs->be->verify_refname_available(refs, refname, extra, skip, err); |
| 1666 | + const char *slash; |
| 1667 | + const char *extra_refname; |
| 1668 | + struct strbuf dirname = STRBUF_INIT; |
| 1669 | + struct strbuf referent = STRBUF_INIT; |
| 1670 | + struct object_id oid; |
| 1671 | + unsigned int type; |
| 1672 | + struct ref_iterator *iter; |
| 1673 | + int ok; |
| 1674 | + int ret = -1; |
| 1675 | + |
| 1676 | + /* |
| 1677 | + * For the sake of comments in this function, suppose that |
| 1678 | + * refname is "refs/foo/bar". |
| 1679 | + */ |
| 1680 | + |
| 1681 | + assert(err); |
| 1682 | + |
| 1683 | + strbuf_grow(&dirname, strlen(refname) + 1); |
| 1684 | + for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) { |
| 1685 | + /* Expand dirname to the new prefix, not including the trailing slash: */ |
| 1686 | + strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len); |
| 1687 | + |
| 1688 | + /* |
| 1689 | + * We are still at a leading dir of the refname (e.g., |
| 1690 | + * "refs/foo"; if there is a reference with that name, |
| 1691 | + * it is a conflict, *unless* it is in skip. |
| 1692 | + */ |
| 1693 | + if (skip && string_list_has_string(skip, dirname.buf)) |
| 1694 | + continue; |
| 1695 | + |
| 1696 | + if (!refs_read_raw_ref(refs, dirname.buf, oid.hash, &referent, &type)) { |
| 1697 | + strbuf_addf(err, "'%s' exists; cannot create '%s'", |
| 1698 | + dirname.buf, refname); |
| 1699 | + goto cleanup; |
| 1700 | + } |
| 1701 | + |
| 1702 | + if (extras && string_list_has_string(extras, dirname.buf)) { |
| 1703 | + strbuf_addf(err, "cannot process '%s' and '%s' at the same time", |
| 1704 | + refname, dirname.buf); |
| 1705 | + goto cleanup; |
| 1706 | + } |
| 1707 | + } |
| 1708 | + |
| 1709 | + /* |
| 1710 | + * We are at the leaf of our refname (e.g., "refs/foo/bar"). |
| 1711 | + * There is no point in searching for a reference with that |
| 1712 | + * name, because a refname isn't considered to conflict with |
| 1713 | + * itself. But we still need to check for references whose |
| 1714 | + * names are in the "refs/foo/bar/" namespace, because they |
| 1715 | + * *do* conflict. |
| 1716 | + */ |
| 1717 | + strbuf_addstr(&dirname, refname + dirname.len); |
| 1718 | + strbuf_addch(&dirname, '/'); |
| 1719 | + |
| 1720 | + iter = refs_ref_iterator_begin(refs, dirname.buf, 0, |
| 1721 | + DO_FOR_EACH_INCLUDE_BROKEN); |
| 1722 | + while ((ok = ref_iterator_advance(iter)) == ITER_OK) { |
| 1723 | + if (skip && |
| 1724 | + string_list_has_string(skip, iter->refname)) |
| 1725 | + continue; |
| 1726 | + |
| 1727 | + strbuf_addf(err, "'%s' exists; cannot create '%s'", |
| 1728 | + iter->refname, refname); |
| 1729 | + ref_iterator_abort(iter); |
| 1730 | + goto cleanup; |
| 1731 | + } |
| 1732 | + |
| 1733 | + if (ok != ITER_DONE) |
| 1734 | + die("BUG: error while iterating over references"); |
| 1735 | + |
| 1736 | + extra_refname = find_descendant_ref(dirname.buf, extras, skip); |
| 1737 | + if (extra_refname) |
| 1738 | + strbuf_addf(err, "cannot process '%s' and '%s' at the same time", |
| 1739 | + refname, extra_refname); |
| 1740 | + else |
| 1741 | + ret = 0; |
| 1742 | + |
| 1743 | +cleanup: |
| 1744 | + strbuf_release(&referent); |
| 1745 | + strbuf_release(&dirname); |
| 1746 | + return ret; |
1666 | 1747 | } |
1667 | 1748 |
|
1668 | 1749 | int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data) |
|
0 commit comments