Add a --stdin signature to read update instructions from standard input
and apply multiple ref updates together. Use an input format that
supports any update that could be specified via the command-line,
including object names like "branch:path with space".
Signed-off-by: Brad King
---
Documentation/git-update-ref.txt | 54 -
builtin/update-ref.c | 252 ++-
2 files changed, 304 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index 0df13ff..0a0a551 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -8,7 +8,7 @@ git-update-ref - Update the object name stored in a ref safely
SYNOPSIS
[verse]
-'git update-ref' [-m ] (-d [] | [--no-deref]
[])
+'git update-ref' [-m ] (-d [] | [--no-deref]
[] | --stdin [-z])
DESCRIPTION
---
@@ -58,6 +58,58 @@ archive by creating a symlink tree).
With `-d` flag, it deletes the named after verifying it
still contains .
+With `--stdin`, update-ref reads instructions from standard input and
+performs all modifications together. Specify commands of the form:
+
+ update SP SP [SP ] LF
+ create SP SP LF
+ delete SP [SP ] LF
+ verify SP [SP ] LF
+ option SP LF
+
+Quote fields containing whitespace as if they were strings in C source
+code. Alternatively, use `-z` to specify commands without quoting:
+
+ update SP NUL NUL [] NUL
+ create SP NUL NUL
+ delete SP NUL [] NUL
+ verify SP NUL [] NUL
+ option SP NUL
+
+Lines of any other format or a repeated produce an error.
+Command meanings are:
+
+update::
+ Set to after verifying , if given.
+ Specify a zero to ensure the ref does not exist
+ after the update and/or a zero to make sure the
+ ref does not exist before the update.
+
+create::
+ Create with after verifying it does not
+ exist. The given may not be zero.
+
+delete::
+ Delete after verifying it exists with , if
+ given. If given, may not be zero.
+
+verify::
+ Verify against but do not change it. If
+zero or missing, the ref must not exist.
+
+option::
+ Modify behavior of the next command naming a .
+ The only valid option is `no-deref` to avoid dereferencing
+ a symbolic ref.
+
+Use 40 "0" or the empty string to specify a zero value, except that
+with `-z` an empty is considered missing.
+
+If all s can be locked with matching s
+simultaneously, all modifications are performed. Otherwise, no
+modifications are performed. Note that while each individual
+ is updated or deleted atomically, a concurrent reader may
+still see a subset of the modifications.
Logging Updates
---
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 7484d36..b5479e2 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -2,23 +2,261 @@
#include "refs.h"
#include "builtin.h"
#include "parse-options.h"
+#include "quote.h"
+#include "argv-array.h"
static const char * const git_update_ref_usage[] = {
N_("git update-ref [options] -d []"),
N_("git update-ref [options] []"),
+ N_("git update-ref [options] --stdin [-z]"),
NULL
};
+static int updates_alloc;
+static int updates_count;
+static const struct ref_update **updates;
+
+static char line_termination = '\n';
+static int update_flags;
+
+static struct ref_update *update_alloc(void)
+{
+ struct ref_update *update;
+
+ /* Allocate and zero-init a struct ref_update */
+ update = xcalloc(1, sizeof(*update));
+ ALLOC_GROW(updates, updates_count + 1, updates_alloc);
+ updates[updates_count++] = update;
+
+ /* Store and reset accumulated options */
+ update->flags = update_flags;
+ update_flags = 0;
+
+ return update;
+}
+
+static void update_store_ref_name(struct ref_update *update,
+ const char *ref_name)
+{
+ if (check_refname_format(ref_name, REFNAME_ALLOW_ONELEVEL))
+ die("invalid ref format: %s", ref_name);
+ update->ref_name = xstrdup(ref_name);
+}
+
+static void update_store_new_sha1(struct ref_update *update,
+ const char *newvalue)
+{
+ if (*newvalue && get_sha1(newvalue, update->new_sha1))
+ die("invalid new value for ref %s: %s",
+ update->ref_name, newvalue);
+}
+
+static void update_store_old_sha1(struct ref_update *update,
+ const char *oldvalue)
+{
+ if (*oldvalue && get_sha1(oldvalue, update->old_sha1))
+ die("invalid old value for ref %s: %s",
+ update->ref_name, oldvalue);
+
+ /* We have an old value if non-empty, or if empty without -z */
+ update->have_old = *oldvalue || line_termination;
+}
+
+static const char *parse_arg(const char *nex