From: Michael Rappazzo <rappazzo@gmail.com>
To: gitster@pobox.com
Cc: git@vger.kernel.org, mhagger@alum.mit.edu, peff@peff.net,
dturner@twopensource.com, pclouds@gmail.com,
sunshine@sunshineco.com, Michael Rappazzo <rappazzo@gmail.com>
Subject: [PATCH 1/5] ff-refs: builtin cmd to check and fast forward local refs to their upstream
Date: Tue, 10 Nov 2015 21:11:21 -0500 [thread overview]
Message-ID: <1447207885-10911-2-git-send-email-rappazzo@gmail.com> (raw)
In-Reply-To: <1447207885-10911-1-git-send-email-rappazzo@gmail.com>
Each local branch with an upstream remote is checked to see if it can be
fast-forwarded to its upstream. If fast-forward applies to the branch,
then this is reported to the user.
The statuses are
UP-TO-DATE - The local branch is the same or equal to the upstream
WOULD-UPDATE - The branch would be fast forwarded
REMOTE-MISSING - The branch is tracking an upstream that is not present
NON-FAST-FORWARD - The branch has diverged from the upstream
Signed-off-by: Michael Rappazzo <rappazzo@gmail.com>
---
.gitignore | 1 +
Makefile | 1 +
builtin.h | 1 +
builtin/ff-refs.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
command-list.txt | 1 +
git.c | 1 +
6 files changed, 226 insertions(+)
create mode 100644 builtin/ff-refs.c
diff --git a/.gitignore b/.gitignore
index 1c2f832..e86a490 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,6 +53,7 @@
/git-difftool--helper
/git-describe
/git-fast-export
+/git-ff-refs
/git-fast-import
/git-fetch
/git-fetch-pack
diff --git a/Makefile b/Makefile
index 43ceeb9..8e312ad 100644
--- a/Makefile
+++ b/Makefile
@@ -853,6 +853,7 @@ BUILTIN_OBJS += builtin/diff.o
BUILTIN_OBJS += builtin/fast-export.o
BUILTIN_OBJS += builtin/fetch-pack.o
BUILTIN_OBJS += builtin/fetch.o
+BUILTIN_OBJS += builtin/ff-refs.o
BUILTIN_OBJS += builtin/fmt-merge-msg.o
BUILTIN_OBJS += builtin/for-each-ref.o
BUILTIN_OBJS += builtin/fsck.o
diff --git a/builtin.h b/builtin.h
index 6b95006..5680e33 100644
--- a/builtin.h
+++ b/builtin.h
@@ -63,6 +63,7 @@ extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
extern int cmd_fast_export(int argc, const char **argv, const char *prefix);
extern int cmd_fetch(int argc, const char **argv, const char *prefix);
extern int cmd_fetch_pack(int argc, const char **argv, const char *prefix);
+extern int cmd_ff_refs(int argc, const char **argv, const char *prefix);
extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
diff --git a/builtin/ff-refs.c b/builtin/ff-refs.c
new file mode 100644
index 0000000..94a4649
--- /dev/null
+++ b/builtin/ff-refs.c
@@ -0,0 +1,221 @@
+#include "cache.h"
+#include "refs.h"
+#include "builtin.h"
+#include "remote.h"
+#include "run-command.h"
+#include "worktree.h"
+
+struct worktree **worktrees;
+const char *padding = ".....................................................";
+
+static const char * const builtin_ff_refs_usage[] = {
+ N_("git ff-refs [<options>]"),
+ NULL
+};
+
+enum ff_result_type {
+ UP_TO_DATE,
+ UPDATABLE,
+ REMOTE_MISSING,
+ NON_FAST_FORWARD,
+ UNABLE_TO_UPDATE
+};
+
+struct ff_ref_details {
+ struct branch *branch;
+ const char *upstream;
+ const char *shortened_upstream;
+ int names_length;
+ enum ff_result_type result_type;
+
+ struct commit *branch_commit;
+ struct commit *upstream_commit;
+ struct commit *merge_base;
+ struct worktree *wt;
+};
+
+struct ff_ref_data {
+ int max_names_length;
+
+ int detail_counter;
+ int detail_alloc;
+ struct ff_ref_details **detail_list;
+};
+
+static const char *result_type_str(enum ff_result_type result_type)
+{
+ switch (result_type) {
+ case UP_TO_DATE:
+ return _("UP-TO-DATE");
+ case UPDATABLE:
+ return _("WOULD-UPDATE");
+ case REMOTE_MISSING:
+ return _("REMOTE-MISSING");
+ case NON_FAST_FORWARD:
+ return _("NON-FAST-FORWARD");
+ default:
+ return _("UNABLE-TO-UPDATE");
+ }
+}
+
+/**
+ * return the worktree with the given refname checked out, or NULL if that
+ * ref is not checked out in any branch.
+ *
+ * This implementation assumes a small number of worktrees (since it loops
+ * through each worktree for every ref). If a repository has a large number
+ * of worktrees, then it might be beneficial to implement this as a hashmap
+ * lookup instead.
+ */
+static struct worktree *find_worktree(const char *refname)
+{
+ int i = 0;
+
+ for (i = 0; worktrees[i]; i++) {
+ if (!worktrees[i]->is_detached && !strcmp(worktrees[i]->head_ref, refname)) {
+ return worktrees[i];
+ }
+ }
+ return NULL;
+}
+
+/**
+ * After all of the relevant refs have been collected, process the
+ * interesting ones
+ */
+static void process_refs(struct ff_ref_data *data)
+{
+ int i = 0;
+
+ for (i = 0; data->detail_list[i]; i++) {
+ struct ff_ref_details *details;
+ int padLen;
+
+ details = data->detail_list[i];
+ padLen = 3 + data->max_names_length - details->names_length;
+ if (padLen < 0)
+ padLen = 0;
+
+ printf(" %s -> %s%*.*s",
+ details->branch->name, details->shortened_upstream, padLen, padLen, padding);
+ printf("[%s]\n", result_type_str(details->result_type));
+ }
+}
+
+static void add_to_detail_list(struct ff_ref_data *data,
+ struct ff_ref_details *details)
+{
+ if (!data->detail_alloc) {
+ data->detail_list = xmalloc(sizeof(struct ff_ref_details *));
+ data->detail_alloc = 1;
+ } else
+ ALLOC_GROW(data->detail_list, data->detail_counter + 1, data->detail_alloc);
+
+ if (details && details->names_length > data->max_names_length)
+ data->max_names_length = details->names_length;
+
+ data->detail_list[data->detail_counter++] = details;
+}
+
+/**
+ * Look for refs which have an upstream configured. Each ref with an upstream
+ * is added to a list to later possibly make changes on. All of the necessary
+ * read-only data is gleaned here.
+ */
+static int analize_refs(const char *refname,
+ const struct object_id *oid, int flags, void *cb_data) {
+
+ struct branch *branch;
+ const char *upstream;
+ struct ff_ref_data *data = cb_data;
+
+ branch = branch_get(shorten_unambiguous_ref(refname, 0));
+ upstream = branch_get_upstream(branch, NULL);
+ if (upstream) {
+ struct ff_ref_details *details = xmalloc(sizeof(struct ff_ref_details));
+ unsigned char upstream_hash[GIT_SHA1_RAWSZ];
+
+ details->branch = branch;
+ details->upstream = upstream;
+
+ details->shortened_upstream = shorten_unambiguous_ref(upstream, 0);
+ details->branch_commit = NULL;
+ details->upstream_commit = NULL;
+ details->merge_base = NULL;
+ details->result_type = UNABLE_TO_UPDATE;
+ details->names_length = strlen(branch->name) +
+ strlen(details->shortened_upstream);
+ details->wt = find_worktree(details->branch->refname);
+
+ if (!resolve_ref_unsafe(details->upstream, RESOLVE_REF_READING,
+ upstream_hash, NULL))
+ details->result_type = REMOTE_MISSING;
+
+ else if (!hashcmp(oid->hash, upstream_hash))
+ details->result_type = UP_TO_DATE;
+ else {
+ struct commit_list *bases;
+
+ details->branch_commit = lookup_commit_reference(oid->hash);
+ details->upstream_commit = lookup_commit_reference(upstream_hash);
+ bases = get_merge_bases(details->branch_commit,
+ details->upstream_commit);
+ details->merge_base = bases->item;
+
+ if (!hashcmp(upstream_hash, details->merge_base->object.sha1))
+ details->result_type = UP_TO_DATE;
+
+ else if (!in_merge_bases(details->branch_commit, details->upstream_commit))
+ details->result_type = NON_FAST_FORWARD;
+
+ else
+ details->result_type = UPDATABLE;
+ }
+ add_to_detail_list(data, details);
+ }
+ return 0;
+}
+
+/**
+ * Free the memory allocated for all of the data
+ */
+static void free_data(struct ff_ref_data *data)
+{
+ int i = 0;
+
+ for (i = 0; data->detail_list[i]; i++)
+ free(data->detail_list[i]);
+ free(data);
+}
+
+int cmd_ff_refs(int argc, const char **argv, const char *prefix)
+{
+ int ret = 0;
+
+ struct option options[] = {
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options, builtin_ff_refs_usage, 0);
+ if (argc)
+ usage_with_options(builtin_ff_refs_usage, options);
+ else {
+ struct ff_ref_data *data = NULL;
+
+ worktrees = get_worktrees();
+ data = xmalloc(sizeof(struct ff_ref_data));
+ data->detail_alloc = 0;
+ data->detail_counter = 0;
+ data->max_names_length = 0;
+
+ ret = for_each_ref(&analize_refs, data);
+ add_to_detail_list(data, NULL);
+
+ //for each detail
+ process_refs(data);
+
+ free_worktrees(worktrees);
+ free_data(data);
+ }
+ return ret;
+}
diff --git a/command-list.txt b/command-list.txt
index 2a94137..b766ea8 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -54,6 +54,7 @@ git-fast-export ancillarymanipulators
git-fast-import ancillarymanipulators
git-fetch mainporcelain remote
git-fetch-pack synchingrepositories
+git-ff-refs mainporcelain history
git-filter-branch ancillarymanipulators
git-fmt-merge-msg purehelpers
git-for-each-ref plumbinginterrogators
diff --git a/git.c b/git.c
index 6ed824c..1c75156 100644
--- a/git.c
+++ b/git.c
@@ -404,6 +404,7 @@ static struct cmd_struct commands[] = {
{ "fast-export", cmd_fast_export, RUN_SETUP },
{ "fetch", cmd_fetch, RUN_SETUP },
{ "fetch-pack", cmd_fetch_pack, RUN_SETUP },
+ { "ff-refs", cmd_ff_refs, RUN_SETUP },
{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
{ "format-patch", cmd_format_patch, RUN_SETUP },
--
2.6.2
next prev parent reply other threads:[~2015-11-11 2:13 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-11-11 2:11 [PATCH 0/5] ff-refs: builtin command to fast-forward local refs Michael Rappazzo
2015-11-11 2:11 ` Michael Rappazzo [this message]
2015-11-11 2:11 ` [PATCH 2/5] ff-refs: update each updatable ref Michael Rappazzo
2015-11-11 2:11 ` [PATCH 3/5] ff-refs: add --dry-run and --skip-worktree options Michael Rappazzo
2015-11-11 2:11 ` [PATCH 4/5] ff-refs: Add documentation Michael Rappazzo
2015-11-11 2:11 ` [PATCH 5/5] ff-refs: Add tests Michael Rappazzo
2015-11-11 10:41 ` [PATCH 0/5] ff-refs: builtin command to fast-forward local refs Michael J Gruber
2015-11-11 12:32 ` Mike Rappazzo
[not found] ` <CANoM8SWxMeDjwy-GwVc+En8D7N8LyzzsBKtX_MbiS4Z49DjD7g@mail.gmail.com>
2015-11-17 15:28 ` Michael J Gruber
2015-11-17 15:36 ` Mike Rappazzo
2015-11-18 9:56 ` Johannes Schindelin
2015-11-24 22:39 ` Jeff King
2015-12-01 0:24 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1447207885-10911-2-git-send-email-rappazzo@gmail.com \
--to=rappazzo@gmail.com \
--cc=dturner@twopensource.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=mhagger@alum.mit.edu \
--cc=pclouds@gmail.com \
--cc=peff@peff.net \
--cc=sunshine@sunshineco.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).