From 5aa6f037642df3b358ab659d17b5fb5bc7936f0f Mon Sep 17 00:00:00 2001
From: Kenneth Cochran <[email protected]>
Date: Sun, 3 Mar 2019 15:05:11 -0600
Subject: [RFC PATCH 2/4] refs: add function to iteratively dereference symref
 chain
Cc: Sahil Dua <[email protected]>,
    Duy Nguyen <[email protected]>,
    Jeff King <[email protected]>

As far as I can tell, currently, there's not really any way
to run something once per symref in a symref chain. This adds that
ability.

This will be useful for instance to improve how `git branch -d`
handles a checked out symref.

Signed-off-by: Kenneth Cochran <[email protected]>
---
 refs.c | 28 ++++++++++++++++++++++++++++
 refs.h | 13 +++++++++++++
 2 files changed, 41 insertions(+)

diff --git a/refs.c b/refs.c
index 142888a40a..18a222d76a 100644
--- a/refs.c
+++ b/refs.c
@@ -1466,6 +1466,34 @@ static int do_for_each_ref(struct ref_store *refs, const 
char *prefix,
                                        do_for_each_ref_helper, &hp);
 }
 
+int refs_for_each_ref_in_chain(struct ref_store *refs, each_ref_fn fn,
+                              void *cb_data, const char *starting_ref)
+{
+       int symref_count;
+       struct object_id oid;
+       int flags;
+       const char *ref_name = xstrdup(starting_ref);
+       int res;
+
+       for (symref_count = 0; symref_count < SYMREF_MAXDEPTH; symref_count++) {
+               res = fn(ref_name, &oid, flags, cb_data);
+               ref_name = refs_resolve_ref_unsafe(refs, ref_name, 
RESOLVE_REF_NO_RECURSE,
+                                                  &oid, &flags);
+
+               if (res)
+                       return res;
+               if (!(flags & REF_ISSYMREF))
+                       break;
+       }
+       return 0;
+}
+
+int for_each_ref_in_chain(each_ref_fn fn, void *cb_data, const char 
*starting_ref)
+{
+       return refs_for_each_ref_in_chain(get_main_ref_store(the_repository), 
fn,
+                                         cb_data, starting_ref);
+}
+
 int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
        return do_for_each_ref(refs, "", fn, 0, 0, cb_data);
diff --git a/refs.h b/refs.h
index 308fa1f03b..66ccb85e8d 100644
--- a/refs.h
+++ b/refs.h
@@ -327,6 +327,19 @@ int for_each_glob_ref_in(each_ref_fn fn, const char 
*pattern,
 int head_ref_namespaced(each_ref_fn fn, void *cb_data);
 int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
 
+/*
+ * Iteratively calls fn with each reference in a symref chain.
+ * Iteration will continue until one of the following occurs:
+ * - SYMREF_MAXDEPTH is reached
+ * - A non-symbolic ref is reached (fn will be called with this before 
returning)
+ * - fn returns a non 0 value
+ *
+ * Will always return 0 unless fn returns a non-zero value.
+ */
+int refs_for_each_ref_in_chain(struct ref_store *refs, each_ref_fn fn,
+                              void *cb_data, const char *starting_ref);
+int for_each_ref_in_chain(each_ref_fn fn, void *cb_data, const char 
*starting_ref);
+
 /* can be used to learn about broken ref and symref */
 int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void 
*cb_data);
 int for_each_rawref(each_ref_fn fn, void *cb_data);
-- 
2.17.1


Reply via email to