From patchwork Tue Aug 27 12:57:54 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Arver via GitGitGadget X-Patchwork-Id: 11116871 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8372E1800 for ; Tue, 27 Aug 2019 12:57:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 540E4206BF for ; Tue, 27 Aug 2019 12:57:59 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="SW/nCPrB" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729882AbfH0M55 (ORCPT ); Tue, 27 Aug 2019 08:57:57 -0400 Received: from mail-wm1-f65.google.com ([209.85.128.65]:51066 "EHLO mail-wm1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727086AbfH0M55 (ORCPT ); Tue, 27 Aug 2019 08:57:57 -0400 Received: by mail-wm1-f65.google.com with SMTP id v15so3002741wml.0 for ; Tue, 27 Aug 2019 05:57:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:message-id:in-reply-to:references:from:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=EFvS5Gi28L2eRTcVRAJTam+GnToN6Ad2o0dwQ+Wr1D8=; b=SW/nCPrBKo1+FjvJGXmd9SnHgQBfFI2YAiSW+gje4RYdG5gif9VBbWqc3oNx8lmPok VKdzHgTSRnwII7gQ/hmOFP5Ya9CCoJIO5JCNJhIY/eBdtDitLTYGPx0+vzNakJxN6Zw2 H+8bqqji5uz2QavvPJVd6Ug5uAHWoe2qaE6NDKxohL+FB20qv8jm6YJtcIwJ8ICUfilJ QQfCe0mP2RvIjq3pSdk6TF9AEzPY2Y+6DRfNlTjIJqIv+pQJtyN2Hzy2bnteC5jqG94r 3f6bjQk8LK08g8UWefyEmn1uG2RTKOZvYvJAvGWYpDNMP63EIafeBXtHoTM+tnuSKLcL m/1w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:message-id:in-reply-to:references:from :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=EFvS5Gi28L2eRTcVRAJTam+GnToN6Ad2o0dwQ+Wr1D8=; b=uO419Add7fDxx75EZn+XeMFsU2qij5puaDVTTxIwHCF9NGKOqba5Ok/5lNApSCI0y3 RmmtKx8BxP28On5JKkdQ2jeIItISX+n1grQjtFd87wAo5/gsVD2Ib2JnOVdnEERCT3WC 2/5wy5Acy6l+1ViKs8aYuUfCccag/qkIreHRSq24tptcQ4OmtGgY/BDynM7OlU2Nn5KD thoHHsdJgER4dBc7PisY1lWHCE9Dr4DSDUIv9KKZeP3ujlMOF9asHQZqdxUookLAs5zT AVEijlcdoAA26I9ilPNjIw89iokwxTcidTpJk9j1R5fCAsMUZRfi7vZQM448yisoDxOA Y2MQ== X-Gm-Message-State: APjAAAWqry5ODD2OBAbm1Y61R02VhLlIFs4wqkCpjlNPTzV7+qvJFQXx 1vsLl8kn8k1YndoMCje20UwS/45fQt0= X-Google-Smtp-Source: APXvYqyEtA5mmXBZVJvv63whGELcXa4BOGBi/nNrHVTZogS09N9NGtG7dLYnOY+E7KYwsHlD0U3C/g== X-Received: by 2002:a1c:20c3:: with SMTP id g186mr28030602wmg.15.1566910674927; Tue, 27 Aug 2019 05:57:54 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id 39sm44504356wrc.45.2019.08.27.05.57.54 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 27 Aug 2019 05:57:54 -0700 (PDT) Date: Tue, 27 Aug 2019 05:57:54 -0700 (PDT) X-Google-Original-Date: Tue, 27 Aug 2019 12:57:42 GMT Message-Id: In-Reply-To: References: From: "Johannes Schindelin via GitGitGadget" Subject: [PATCH v4 01/11] Start to implement a built-in version of `git add --interactive` Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: Jeff Hostetler , Jeff King , Junio C Hamano , Johannes Schindelin Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Johannes Schindelin This is hardly the first conversion of a Git command that is implemented as a script to a built-in. So far, the most successful strategy for such conversions has been to add a built-in helper and call that for more and more functionality from the script, as more and more parts are converted. With the interactive add, we choose a different strategy. The sole reason for this is that on Windows (where such a conversion has the most benefits in terms of speed and robustness) we face the very specific problem that a `system()` call in Perl seems to close `stdin` in the parent process when the spawned process consumes even one character from `stdin`. And that just does not work for us here, as it would stop the main loop as soon as any interactive command was performed by the helper. Which is almost all of the commands in `git add -i`. It is almost as if Perl told us once again that it does not want us to use it on Windows. Instead, we follow the opposite route where we start with a bare-bones version of the built-in interactive add, guarded by the new `add.interactive.useBuiltin` config variable, and then add more and more functionality to it, until it is feature complete. At this point, the built-in version of `git add -i` only states that it cannot do anything yet ;-) Signed-off-by: Johannes Schindelin --- Documentation/config/add.txt | 5 +++++ Makefile | 1 + add-interactive.c | 7 +++++++ add-interactive.h | 8 ++++++++ builtin/add.c | 10 ++++++++++ t/README | 4 ++++ 6 files changed, 35 insertions(+) create mode 100644 add-interactive.c create mode 100644 add-interactive.h diff --git a/Documentation/config/add.txt b/Documentation/config/add.txt index 4d753f006e..c9f748f81c 100644 --- a/Documentation/config/add.txt +++ b/Documentation/config/add.txt @@ -5,3 +5,8 @@ add.ignore-errors (deprecated):: option of linkgit:git-add[1]. `add.ignore-errors` is deprecated, as it does not follow the usual naming convention for configuration variables. + +add.interactive.useBuiltin:: + [EXPERIMENTAL] Set to `true` to use the experimental built-in + implementation of the interactive version of linkgit:git-add[1] + instead of the Perl script version. Is `false` by default. diff --git a/Makefile b/Makefile index f9255344ae..d04daf9fd8 100644 --- a/Makefile +++ b/Makefile @@ -826,6 +826,7 @@ LIB_H := $(sort $(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null -name '*.h' -print)) LIB_OBJS += abspath.o +LIB_OBJS += add-interactive.o LIB_OBJS += advice.o LIB_OBJS += alias.o LIB_OBJS += alloc.o diff --git a/add-interactive.c b/add-interactive.c new file mode 100644 index 0000000000..482e458dc6 --- /dev/null +++ b/add-interactive.c @@ -0,0 +1,7 @@ +#include "cache.h" +#include "add-interactive.h" + +int run_add_i(struct repository *r, const struct pathspec *ps) +{ + die(_("No commands are available in the built-in `git add -i` yet!")); +} diff --git a/add-interactive.h b/add-interactive.h new file mode 100644 index 0000000000..7043b8741d --- /dev/null +++ b/add-interactive.h @@ -0,0 +1,8 @@ +#ifndef ADD_INTERACTIVE_H +#define ADD_INTERACTIVE_H + +struct repository; +struct pathspec; +int run_add_i(struct repository *r, const struct pathspec *ps); + +#endif diff --git a/builtin/add.c b/builtin/add.c index dd18e5c9b6..4f625691b5 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -20,6 +20,7 @@ #include "bulk-checkin.h" #include "argv-array.h" #include "submodule.h" +#include "add-interactive.h" static const char * const builtin_add_usage[] = { N_("git add [] [--] ..."), @@ -185,6 +186,14 @@ int run_add_interactive(const char *revision, const char *patch_mode, { int status, i; struct argv_array argv = ARGV_ARRAY_INIT; + int use_builtin_add_i = + git_env_bool("GIT_TEST_ADD_I_USE_BUILTIN", -1); + if (use_builtin_add_i < 0) + git_config_get_bool("add.interactive.usebuiltin", + &use_builtin_add_i); + + if (use_builtin_add_i == 1 && !patch_mode) + return !!run_add_i(the_repository, pathspec); argv_array_push(&argv, "add--interactive"); if (patch_mode) @@ -319,6 +328,7 @@ static int add_config(const char *var, const char *value, void *cb) ignore_add_errors = git_config_bool(var, value); return 0; } + return git_default_config(var, value, cb); } diff --git a/t/README b/t/README index 60d5b77bcc..bda93fe603 100644 --- a/t/README +++ b/t/README @@ -397,6 +397,10 @@ GIT_TEST_STASH_USE_BUILTIN=, when false, disables the built-in version of git-stash. See 'stash.useBuiltin' in git-config(1). +GIT_TEST_ADD_I_USE_BUILTIN=, when true, enables the +builtin version of git add -i. See 'add.interactive.useBuiltin' in +git-config(1). + GIT_TEST_INDEX_THREADS= enables exercising the multi-threaded loading of the index for the whole test suite by bypassing the default number of cache entries and thread minimums. Setting this to 1 will make the From patchwork Tue Aug 27 12:57:55 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Arver via GitGitGadget X-Patchwork-Id: 11116889 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3BB92174A for ; Tue, 27 Aug 2019 12:58:10 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 1A08C2077B for ; Tue, 27 Aug 2019 12:58:10 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="q+BrlX1y" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729947AbfH0M6F (ORCPT ); Tue, 27 Aug 2019 08:58:05 -0400 Received: from mail-wr1-f54.google.com ([209.85.221.54]:35846 "EHLO mail-wr1-f54.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728188AbfH0M56 (ORCPT ); Tue, 27 Aug 2019 08:57:58 -0400 Received: by mail-wr1-f54.google.com with SMTP id y19so851110wrd.3 for ; Tue, 27 Aug 2019 05:57:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:message-id:in-reply-to:references:from:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=l25ies6dv1TGwp/ChEvj6ryxriJQU9OfmiV2WryrGnE=; b=q+BrlX1yGjauO442Nuyy6Zv6TPT8PuK9mk3+KBQDqHisdSRWMnGhI4Uc1yDBTnjd4c zlLne3bPknee8b+E4R2j62cwgVxvDUppwtBWq18NJiLTH5qeIGq5hBci4WcU95GMHhSz Uc3GQoAas6Wi3GhCKi+b8bxz2gvXhsSg647iLlBUMswHB62njyHX2FVGY9a2DgJQA/bC G7sJ6Y9RewR8zbJUPCLyIOjho7Irqg7TmH4O5tgiMlg1mxY7qP39/z+oQygYbrYvZJiB XLuT7VQwz0JsXDEyhhG++K0crpUVbjK9bdztPrDbqfucRqyF0ABAbbVkJGeLDt3sqO/w 7B7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:message-id:in-reply-to:references:from :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=l25ies6dv1TGwp/ChEvj6ryxriJQU9OfmiV2WryrGnE=; b=l5hiu+ElTcEPI6suo3A4kbYjJBD9qhimCd6Fd5uMiwtU2adIle0vVr4/aTxW0v+Mbu I3Y7KleeQRH6ei3GGw92RS06lpohjcMLKb2xPW3rm1fKDhiMTdb3o+P79C3oMcWjiyNn /TqszO1F8MYbqhOKrXtwAuNRyG1YOFnYXMyI7Z9bRa01gNio9NBXoeNbJ435HKmTpbob cSgQ5Y+84u1aj5hjAyyEWb/7UCLNF7x2TcGis+MbncQQqiyrYE3xUbB+fpbhCvxnwOvZ Iu3TKpPkQbWNkdcfsA8IsS2LJIOguBeJn4Kpk3JusAwYJEKUZD7mqsCzqlPLrdYm9h0p m4yg== X-Gm-Message-State: APjAAAWX5NK/Earh5mXw0p/64LiDGhZ39hNxOaUf9oGzDWrI/mv7vdPE unlKom+UKVm5I7v17m/qQWTzxwxxGjM= X-Google-Smtp-Source: APXvYqzV74N4+rWgXczF62tvW0gXjDcSajQyFzCrZAGiGRzmfo6n+ZmOVHVjLAyWBdrCuIltfMD3jg== X-Received: by 2002:a5d:6606:: with SMTP id n6mr3829903wru.346.1566910675804; Tue, 27 Aug 2019 05:57:55 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id f10sm12821459wrs.22.2019.08.27.05.57.55 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 27 Aug 2019 05:57:55 -0700 (PDT) Date: Tue, 27 Aug 2019 05:57:55 -0700 (PDT) X-Google-Original-Date: Tue, 27 Aug 2019 12:57:43 GMT Message-Id: <38cc04c1d9816bc3bad960df0d5edea3020a9173.1566910672.git.gitgitgadget@gmail.com> In-Reply-To: References: From: "Daniel Ferreira via GitGitGadget" Subject: [PATCH v4 02/11] diff: export diffstat interface Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: Jeff Hostetler , Jeff King , Junio C Hamano , Daniel Ferreira Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Daniel Ferreira Make the diffstat interface (namely, the diffstat_t struct and compute_diffstat) no longer be internal to diff.c and allow it to be used by other parts of git. This is helpful for code that may want to easily extract information from files using the diff machinery, while flushing it differently from how the show_* functions used by diff_flush() do it. One example is the builtin implementation of git-add--interactive's status. Signed-off-by: Daniel Ferreira Signed-off-by: Slavica Djukic Signed-off-by: Johannes Schindelin --- diff.c | 39 ++++++++++++++++----------------------- diff.h | 20 ++++++++++++++++++++ 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/diff.c b/diff.c index efe42b341a..6829a74065 100644 --- a/diff.c +++ b/diff.c @@ -2492,22 +2492,6 @@ static void pprint_rename(struct strbuf *name, const char *a, const char *b) } } -struct diffstat_t { - int nr; - int alloc; - struct diffstat_file { - char *from_name; - char *name; - char *print_name; - const char *comments; - unsigned is_unmerged:1; - unsigned is_binary:1; - unsigned is_renamed:1; - unsigned is_interesting:1; - uintmax_t added, deleted; - } **files; -}; - static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat, const char *name_a, const char *name_b) @@ -3154,7 +3138,7 @@ static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *o gather_dirstat(options, &dir, changed, "", 0); } -static void free_diffstat_info(struct diffstat_t *diffstat) +void free_diffstat_info(struct diffstat_t *diffstat) { int i; for (i = 0; i < diffstat->nr; i++) { @@ -6278,12 +6262,7 @@ void diff_flush(struct diff_options *options) dirstat_by_line) { struct diffstat_t diffstat; - memset(&diffstat, 0, sizeof(struct diffstat_t)); - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - if (check_pair_status(p)) - diff_flush_stat(p, options, &diffstat); - } + compute_diffstat(options, &diffstat, q); if (output_format & DIFF_FORMAT_NUMSTAT) show_numstat(&diffstat, options); if (output_format & DIFF_FORMAT_DIFFSTAT) @@ -6616,6 +6595,20 @@ static int is_submodule_ignored(const char *path, struct diff_options *options) return ignored; } +void compute_diffstat(struct diff_options *options, + struct diffstat_t *diffstat, + struct diff_queue_struct *q) +{ + int i; + + memset(diffstat, 0, sizeof(struct diffstat_t)); + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + if (check_pair_status(p)) + diff_flush_stat(p, options, diffstat); + } +} + void diff_addremove(struct diff_options *options, int addremove, unsigned mode, const struct object_id *oid, diff --git a/diff.h b/diff.h index c2c3056810..c791ecc972 100644 --- a/diff.h +++ b/diff.h @@ -245,6 +245,22 @@ void diff_emit_submodule_error(struct diff_options *o, const char *err); void diff_emit_submodule_pipethrough(struct diff_options *o, const char *line, int len); +struct diffstat_t { + int nr; + int alloc; + struct diffstat_file { + char *from_name; + char *name; + char *print_name; + const char *comments; + unsigned is_unmerged:1; + unsigned is_binary:1; + unsigned is_renamed:1; + unsigned is_interesting:1; + uintmax_t added, deleted; + } **files; +}; + enum color_diff { DIFF_RESET = 0, DIFF_CONTEXT = 1, @@ -334,6 +350,10 @@ void diff_change(struct diff_options *, struct diff_filepair *diff_unmerge(struct diff_options *, const char *path); +void compute_diffstat(struct diff_options *options, struct diffstat_t *diffstat, + struct diff_queue_struct *q); +void free_diffstat_info(struct diffstat_t *diffstat); + #define DIFF_SETUP_REVERSE 1 #define DIFF_SETUP_USE_SIZE_CACHE 4 From patchwork Tue Aug 27 12:57:56 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Arver via GitGitGadget X-Patchwork-Id: 11116877 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id ACD54174A for ; Tue, 27 Aug 2019 12:58:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 815D62077B for ; Tue, 27 Aug 2019 12:58:03 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ftPTfaUc" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729931AbfH0M6C (ORCPT ); Tue, 27 Aug 2019 08:58:02 -0400 Received: from mail-wm1-f66.google.com ([209.85.128.66]:33511 "EHLO mail-wm1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729860AbfH0M57 (ORCPT ); Tue, 27 Aug 2019 08:57:59 -0400 Received: by mail-wm1-f66.google.com with SMTP id p77so2372112wme.0 for ; Tue, 27 Aug 2019 05:57:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:message-id:in-reply-to:references:from:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=P4P7QG68xUN7caKtNHmnOMx1LT8AjZMwNtE3Jy4TSg4=; b=ftPTfaUcXsl4ySxJ2xHy331dreZ/sBQ1EerfEYHrZ7PKo34GVL5VdeObR1NfWGxugR EOVLdTM8Jn1tP2yDoixk0zBjT20SkRB7URUyBYlLdG5oIlNYeFoi0r+W0KiYS6GFgETU Dv9Uh0LnZFYz8wESq8rfs4Cs8RT0Nf1OnRyJx6+J8sN9rBwPLQG5RonFyKtcczhLHEnA 3kGv22qf8iC49Jke34UKQSGQ57WL8qK3ssTkYdx/CCydRTdWbmPQckGc5QN4/M4p/Yzm rpACZhlqeUiZ2ijkevpOcU525JPRvvVkmLvEsiYaBeqnfTyXoPkoQlus+QqT0dNi2psZ WpcA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:message-id:in-reply-to:references:from :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=P4P7QG68xUN7caKtNHmnOMx1LT8AjZMwNtE3Jy4TSg4=; b=JCp9md8JxFoKQU8CBm8k4OsL4sZ68hshwBtlQ5C8R31amD674uWhO7RlGCv71YEZTY pZ1B5ofmzziOEY+ZK6+uh5GrhdoY415PVqnL6RnIyE+k+KNhq/KHWgO9YWDUGWHjTtxV 7I3kHLwWdLEZEnPg6ciZCvPYrvK3m9BN2170EK90quoRyb8GMo4nTOtk2YmNLxUke4j/ Y2IibARJw0bxx1GsxqZVf0tuKOKks5NsjQfw9trj/G400p4bgrs7V4jWu7NigFDQSriq E9Ivx85Al+SUC9UOnk1zXd5pj96hBkFlCPKHysKws0QzTd+ZRbT0rxHqwRNeKpxGWush 2Llg== X-Gm-Message-State: APjAAAVuL5A4QYVnu5vyXDvudx+uqAz2AgOVi6C0cxaoasgU6IZuGEzR 1QfN1XjWwGg+13iz1+WuA7IplSIkSBw= X-Google-Smtp-Source: APXvYqxWEhEXNpKgUNGE4p7ZiMby0otuTU1zHdUA1tENuLUtvNrFBjkNswwsjv/6yHo1qnWgqS3pdw== X-Received: by 2002:a1c:a7c9:: with SMTP id q192mr27274307wme.144.1566910676512; Tue, 27 Aug 2019 05:57:56 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id o2sm3529290wmh.9.2019.08.27.05.57.55 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 27 Aug 2019 05:57:56 -0700 (PDT) Date: Tue, 27 Aug 2019 05:57:56 -0700 (PDT) X-Google-Original-Date: Tue, 27 Aug 2019 12:57:44 GMT Message-Id: In-Reply-To: References: From: "Daniel Ferreira via GitGitGadget" Subject: [PATCH v4 03/11] built-in add -i: implement the `status` command Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: Jeff Hostetler , Jeff King , Junio C Hamano , Daniel Ferreira Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Daniel Ferreira This implements the `status` command of `git add -i`. The data structures introduced in this commit will be extended as needed later. At this point, we re-implement only part of the `list_and_choose()` function of the Perl script `git-add--interactive.perl` and call it `list()`. It does not yet color anything, or do columns, or allow user input. Over the course of the next commits, we will introduce a `list_and_choose()` function that uses `list()` to display the list of options and let the user choose one or more of the displayed items. This will be used to implement the main loop of the built-in `git add -i`, at which point the new `status` command can actually be used. Note that we pass the list of items as a `struct item **` as opposed to a `struct item *`, to allow for the actual items to contain much more information than merely the name. Signed-off-by: Daniel Ferreira Signed-off-by: Slavica Djukic Signed-off-by: Johannes Schindelin --- add-interactive.c | 266 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 265 insertions(+), 1 deletion(-) diff --git a/add-interactive.c b/add-interactive.c index 482e458dc6..d64206ba1c 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -1,7 +1,271 @@ #include "cache.h" #include "add-interactive.h" +#include "diffcore.h" +#include "revision.h" +#include "refs.h" + +struct item { + const char *name; +}; + +struct list_options { + const char *header; + void (*print_item)(int i, struct item *item, void *print_item_data); + void *print_item_data; +}; + +static void list(struct item **list, size_t nr, struct list_options *opts) +{ + int i; + + if (!nr) + return; + + if (opts->header) + printf("%s\n", opts->header); + + for (i = 0; i < nr; i++) { + opts->print_item(i, list[i], opts->print_item_data); + putchar('\n'); + } +} + +struct adddel { + uintmax_t add, del; + unsigned seen:1, binary:1; +}; + +struct file_list { + struct file_item { + struct item item; + struct adddel index, worktree; + } **file; + size_t nr, alloc; +}; + +static void add_file_item(struct file_list *list, const char *name) +{ + struct file_item *item; + + FLEXPTR_ALLOC_STR(item, item.name, name); + + ALLOC_GROW(list->file, list->nr + 1, list->alloc); + list->file[list->nr++] = item; +} + +static void reset_file_list(struct file_list *list) +{ + size_t i; + + for (i = 0; i < list->nr; i++) + free(list->file[i]); + list->nr = 0; +} + +static void release_file_list(struct file_list *list) +{ + reset_file_list(list); + FREE_AND_NULL(list->file); + list->alloc = 0; +} + +static int file_item_cmp(const void *a, const void *b) +{ + const struct file_item * const *f1 = a; + const struct file_item * const *f2 = b; + + return strcmp((*f1)->item.name, (*f2)->item.name); +} + +struct pathname_entry { + struct hashmap_entry ent; + size_t index; + char pathname[FLEX_ARRAY]; +}; + +static int pathname_entry_cmp(const void *unused_cmp_data, + const void *entry, const void *entry_or_key, + const void *pathname) +{ + const struct pathname_entry *e1 = entry, *e2 = entry_or_key; + + return strcmp(e1->pathname, + pathname ? (const char *)pathname : e2->pathname); +} + +struct collection_status { + enum { FROM_WORKTREE = 0, FROM_INDEX = 1 } phase; + + const char *reference; + + struct file_list *list; + struct hashmap file_map; +}; + +static void collect_changes_cb(struct diff_queue_struct *q, + struct diff_options *options, + void *data) +{ + struct collection_status *s = data; + struct diffstat_t stat = { 0 }; + int i; + + if (!q->nr) + return; + + compute_diffstat(options, &stat, q); + + for (i = 0; i < stat.nr; i++) { + const char *name = stat.files[i]->name; + int hash = strhash(name); + struct pathname_entry *entry; + size_t file_index; + struct file_item *file; + struct adddel *adddel; + + entry = hashmap_get_from_hash(&s->file_map, hash, name); + if (entry) + file_index = entry->index; + else { + FLEX_ALLOC_STR(entry, pathname, name); + hashmap_entry_init(&entry->ent, hash); + entry->index = file_index = s->list->nr; + hashmap_add(&s->file_map, &entry->ent); + + add_file_item(s->list, name); + } + file = s->list->file[file_index]; + + adddel = s->phase == FROM_INDEX ? &file->index : &file->worktree; + adddel->seen = 1; + adddel->add = stat.files[i]->added; + adddel->del = stat.files[i]->deleted; + if (stat.files[i]->is_binary) + adddel->binary = 1; + } + free_diffstat_info(&stat); +} + +static int get_modified_files(struct repository *r, struct file_list *list, + const struct pathspec *ps) +{ + struct object_id head_oid; + int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, + &head_oid, NULL); + struct collection_status s = { FROM_WORKTREE }; + + if (repo_read_index_preload(r, ps, 0) < 0) + return error(_("could not read index")); + + s.list = list; + hashmap_init(&s.file_map, pathname_entry_cmp, NULL, 0); + + for (s.phase = FROM_WORKTREE; s.phase <= FROM_INDEX; s.phase++) { + struct rev_info rev; + struct setup_revision_opt opt = { 0 }; + + opt.def = is_initial ? + empty_tree_oid_hex() : oid_to_hex(&head_oid); + + init_revisions(&rev, NULL); + setup_revisions(0, NULL, &rev, &opt); + + rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; + rev.diffopt.format_callback = collect_changes_cb; + rev.diffopt.format_callback_data = &s; + + if (ps) + copy_pathspec(&rev.prune_data, ps); + + if (s.phase == FROM_INDEX) + run_diff_index(&rev, 1); + else { + rev.diffopt.flags.ignore_dirty_submodules = 1; + run_diff_files(&rev, 0); + } + } + hashmap_free(&s.file_map, 1); + + /* While the diffs are ordered already, we ran *two* diffs... */ + QSORT(list->file, list->nr, file_item_cmp); + + return 0; +} + +static void populate_wi_changes(struct strbuf *buf, + struct adddel *ad, const char *no_changes) +{ + if (ad->binary) + strbuf_addstr(buf, _("binary")); + else if (ad->seen) + strbuf_addf(buf, "+%"PRIuMAX"/-%"PRIuMAX, + (uintmax_t)ad->add, (uintmax_t)ad->del); + else + strbuf_addstr(buf, no_changes); +} + +struct print_file_item_data { + const char *modified_fmt; + struct strbuf buf, index, worktree; +}; + +static void print_file_item(int i, struct item *item, + void *print_file_item_data) +{ + struct file_item *c = (struct file_item *)item; + struct print_file_item_data *d = print_file_item_data; + + strbuf_reset(&d->index); + strbuf_reset(&d->worktree); + strbuf_reset(&d->buf); + + populate_wi_changes(&d->worktree, &c->worktree, _("nothing")); + populate_wi_changes(&d->index, &c->index, _("unchanged")); + strbuf_addf(&d->buf, d->modified_fmt, + d->index.buf, d->worktree.buf, item->name); + + printf(" %2d: %s", i + 1, d->buf.buf); +} + +static int run_status(struct repository *r, const struct pathspec *ps, + struct file_list *files, struct list_options *opts) +{ + reset_file_list(files); + + if (get_modified_files(r, files, ps) < 0) + return -1; + + if (files->nr) + list((struct item **)files->file, files->nr, opts); + putchar('\n'); + + return 0; +} int run_add_i(struct repository *r, const struct pathspec *ps) { - die(_("No commands are available in the built-in `git add -i` yet!")); + struct print_file_item_data print_file_item_data = { + "%12s %12s %s", STRBUF_INIT, STRBUF_INIT, STRBUF_INIT + }; + struct list_options opts = { + NULL, print_file_item, &print_file_item_data + }; + struct strbuf header = STRBUF_INIT; + struct file_list files = { NULL }; + int res = 0; + + strbuf_addstr(&header, " "); + strbuf_addf(&header, print_file_item_data.modified_fmt, + _("staged"), _("unstaged"), _("path")); + opts.header = header.buf; + + res = run_status(r, ps, &files, &opts); + + release_file_list(&files); + strbuf_release(&print_file_item_data.buf); + strbuf_release(&print_file_item_data.index); + strbuf_release(&print_file_item_data.worktree); + strbuf_release(&header); + + return res; } From patchwork Tue Aug 27 12:57:56 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Arver via GitGitGadget X-Patchwork-Id: 11116881 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C7DA313B1 for ; Tue, 27 Aug 2019 12:58:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id A4721206BF for ; Tue, 27 Aug 2019 12:58:05 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="WsA0QQcc" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729153AbfH0M6E (ORCPT ); Tue, 27 Aug 2019 08:58:04 -0400 Received: from mail-wr1-f68.google.com ([209.85.221.68]:44293 "EHLO mail-wr1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727086AbfH0M57 (ORCPT ); Tue, 27 Aug 2019 08:57:59 -0400 Received: by mail-wr1-f68.google.com with SMTP id p17so18690911wrf.11 for ; Tue, 27 Aug 2019 05:57:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:message-id:in-reply-to:references:from:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=ddx1qp7yobgLQ1SEJr4onXZfVjSpKH9xoqGS2zK+XO0=; b=WsA0QQccC+wbMF2FNinefK459tmAct9Ox4TMX967HZgiCu31G8yxXSDERhlA7T7tL8 j69K4Ol6fCYQpLU1RqMQ+nB2Dg6rZYwckVNvWS4shREQ35bwov6LCW5WWl43GprwL7Qs avZRDDT1RgGegs/Hs6CeSW/xh/aq8pqj1MI7cXdfua4aI3BdeO6YD3JKRlsjXRfNY8lC W0Knh0BFVjU4wtpqLMLT8ABs/2RGDBTWRyVsyAsXjqyrLdeEX2r8W77USi46O35pZXzB /Pu59z7VmON+lNQ1XHTUuoS3D7cq5HlI/4pCS6DfYB/cevRcJfJS1p9oJW57xB2zdZkt BO3Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:message-id:in-reply-to:references:from :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=ddx1qp7yobgLQ1SEJr4onXZfVjSpKH9xoqGS2zK+XO0=; b=lLt2mFGsOD/FbSl10ZB8/llbWyZyT6cKSi/Sy75ZUWp/OaDf7i41fUKt7HePi6X5/R MzOZCVWwBqt88D/I1WJdmNBr9Vf+I5nnfAm8AUM7fF8iFnjx7ddmVFAe4cD4cFmrKyFH 1fCdVQckKxbP/Izmb95YIlqioXCdduug9qQ6AqGNr6Zy62rN86Xe8U0gJHyu1DC4UXuB 2w3g1PVWn9F8bOy8wsGsjeKz1qKsZvbrjjixPJVrAymTXs95wPYlwhrMqBFTp3bqPhvP 8pBGPbLqMASGehhZ9mI7h7SdDIYNgtDIbcUJ3GbGZSoFatcD+9RtoLew4Vi67yA862S4 cF0g== X-Gm-Message-State: APjAAAVJuzmC1kARNLMA54+hYZ/878eBY5JRQRfxy+d/NIuiiimQQQJI mc3PJResl3A59Kkeg9NzoZWf+RIjqSk= X-Google-Smtp-Source: APXvYqyzB7uZQqce6iEHig6TWpS5YoBWBR4OBEHCglwCo5Rx7nepJ6MnhMoLfsEDDxvFVum6X5ERRQ== X-Received: by 2002:adf:9222:: with SMTP id 31mr30162792wrj.93.1566910677142; Tue, 27 Aug 2019 05:57:57 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id f17sm3764108wmj.27.2019.08.27.05.57.56 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 27 Aug 2019 05:57:56 -0700 (PDT) Date: Tue, 27 Aug 2019 05:57:56 -0700 (PDT) X-Google-Original-Date: Tue, 27 Aug 2019 12:57:45 GMT Message-Id: <3c855d9fa563a288d7934e3cca29295fc929257a.1566910672.git.gitgitgadget@gmail.com> In-Reply-To: References: From: "Johannes Schindelin via GitGitGadget" Subject: [PATCH v4 04/11] built-in add -i: refresh the index before running `status` Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: Jeff Hostetler , Jeff King , Junio C Hamano , Johannes Schindelin Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Johannes Schindelin This is what the Perl version does, and therefore it is what the built-in version should do, too. Signed-off-by: Johannes Schindelin --- add-interactive.c | 4 +++- repository.c | 19 +++++++++++++++++++ repository.h | 7 +++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/add-interactive.c b/add-interactive.c index d64206ba1c..427abe505e 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -259,7 +259,9 @@ int run_add_i(struct repository *r, const struct pathspec *ps) _("staged"), _("unstaged"), _("path")); opts.header = header.buf; - res = run_status(r, ps, &files, &opts); + repo_refresh_and_write_index(r, REFRESH_QUIET, 1); + if (run_status(r, ps, &files, &opts) < 0) + res = -1; release_file_list(&files); strbuf_release(&print_file_item_data.buf); diff --git a/repository.c b/repository.c index 682c239fe3..def35c40fc 100644 --- a/repository.c +++ b/repository.c @@ -275,3 +275,22 @@ int repo_hold_locked_index(struct repository *repo, BUG("the repo hasn't been setup"); return hold_lock_file_for_update(lf, repo->index_file, flags); } + +int repo_refresh_and_write_index(struct repository *r, + unsigned int flags, int gentle) +{ + struct lock_file lock_file = LOCK_INIT; + int fd; + + if (repo_read_index_preload(r, NULL, 0) < 0) + return error(_("could not read index")); + fd = repo_hold_locked_index(r, &lock_file, 0); + if (!gentle && fd < 0) + return error(_("could not lock index for writing")); + refresh_index(r->index, flags, NULL, NULL, NULL); + if (0 <= fd) + repo_update_index_if_able(r, &lock_file); + rollback_lock_file(&lock_file); + + return 0; +} diff --git a/repository.h b/repository.h index 4fb6a5885f..cf5d5bab48 100644 --- a/repository.h +++ b/repository.h @@ -157,5 +157,12 @@ int repo_read_index_unmerged(struct repository *); */ void repo_update_index_if_able(struct repository *, struct lock_file *); +/* + * Refresh the index and write it out. If the index file could not be + * locked, error out, except in gentle mode. The flags will be passed + * through to refresh_index(). + */ +int repo_refresh_and_write_index(struct repository *r, + unsigned int flags, int gentle); #endif /* REPOSITORY_H */ From patchwork Tue Aug 27 12:57:57 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Arver via GitGitGadget X-Patchwork-Id: 11116893 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 09FD913B1 for ; Tue, 27 Aug 2019 12:58:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D9D13206BF for ; Tue, 27 Aug 2019 12:58:12 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="lsNWe7HV" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729919AbfH0M6C (ORCPT ); Tue, 27 Aug 2019 08:58:02 -0400 Received: from mail-wm1-f65.google.com ([209.85.128.65]:39288 "EHLO mail-wm1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726278AbfH0M6B (ORCPT ); Tue, 27 Aug 2019 08:58:01 -0400 Received: by mail-wm1-f65.google.com with SMTP id i63so2915658wmg.4 for ; Tue, 27 Aug 2019 05:57:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:message-id:in-reply-to:references:from:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=gTgvDKX2gGOcJL+yrNvDiMNo7ZrmoA65OM4l2VLC7yw=; b=lsNWe7HV8pMxuGg/YO7UgQiXNx5ceVD0kwWH6beOdIF3KhRFMyGRdXjrejVOqPDKqt YQdRjWu85JEsmmyQw7BoMiuPIZsUw+yHR5Igywmt66L1w5YhhUNKcmyXbphkQFTILuuS HNEHrEH4Lt56Bn0SG7uclQPHyGo7+YGTDOJSYV1lCvx3e25yOCpY3onA9H/Fkl3EbTOY zvx7Nh4ZCReGOsO9jTohCiM02yWwM8SMuqcl1g7KL0nNGHugPuaFsloSa4EQT3BgiFtQ ZmuxDAveUJDyULYZKZ6YUPVrzDCk7s/SLkaGAOV2oRuAnz9RSNPhdvgSe786X2p2mQIj TzHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:message-id:in-reply-to:references:from :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=gTgvDKX2gGOcJL+yrNvDiMNo7ZrmoA65OM4l2VLC7yw=; b=HEmGsVZznNDTas0sSm0mrAERuAyCIZ0zsOVuWeZL8MWjgX0EIQVSFMJ3eDPklmOB5A JG5sj0k6bWNc05tBkbyAb0/FeSb+CEPVOaLYugi3Ls5IYZUnRVZypHt0GXMWLOaAKsJi i++Devj5ceDQmRHUZHqwf0em5vL3RJpm1JsDRveC6TbsuXiptfQlKekIRYUc4m2bBdat s4AU1E/rGbOPYCe4PRdEDhCFUcw8jo32WHmquJaKMF1o2k7rLjJ1lEz8FluxUwmW6/qh S5A31+q2wZhuKq/g8SMrpYSUvWVhIY8gRzhxfwpoyCRqy7n3z41LZ5qfbI2088b9K97p /vxg== X-Gm-Message-State: APjAAAXQ5SxYnT+c9HaNHta5115Z3bB/2kHvHjy0ZN6425clBS6Djx2B LVJNZUA+KskDZ3sOox9yQ26Tonze8No= X-Google-Smtp-Source: APXvYqyp5xEZQTFBRirTwKL0a1Q1LeE3MWRaHNzogkizKwBg6RMJbTg7H1iJusJQJ1nQPygfVVEvvw== X-Received: by 2002:a05:600c:2102:: with SMTP id u2mr29151726wml.105.1566910677816; Tue, 27 Aug 2019 05:57:57 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id o9sm12853343wrj.17.2019.08.27.05.57.57 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 27 Aug 2019 05:57:57 -0700 (PDT) Date: Tue, 27 Aug 2019 05:57:57 -0700 (PDT) X-Google-Original-Date: Tue, 27 Aug 2019 12:57:46 GMT Message-Id: <24737a09f7d2f82a6d19bdfff846af32c4fb0aa4.1566910672.git.gitgitgadget@gmail.com> In-Reply-To: References: From: "Johannes Schindelin via GitGitGadget" Subject: [PATCH v4 05/11] built-in add -i: color the header in the `status` command Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: Jeff Hostetler , Jeff King , Junio C Hamano , Johannes Schindelin Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Johannes Schindelin For simplicity, we only implemented the `status` command without colors. This patch starts adding color, matching what the Perl script `git-add--interactive.perl` does. Original-Patch-By: Daniel Ferreira Signed-off-by: Slavica Djukic Signed-off-by: Johannes Schindelin --- add-interactive.c | 60 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/add-interactive.c b/add-interactive.c index 427abe505e..f5577a80d3 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -1,9 +1,51 @@ #include "cache.h" #include "add-interactive.h" +#include "color.h" +#include "config.h" #include "diffcore.h" #include "revision.h" #include "refs.h" +struct add_i_state { + struct repository *r; + int use_color; + char header_color[COLOR_MAXLEN]; +}; + +static void init_color(struct repository *r, struct add_i_state *s, + const char *slot_name, char *dst, + const char *default_color) +{ + char *key = xstrfmt("color.interactive.%s", slot_name); + const char *value; + + if (!s->use_color) + dst[0] = '\0'; + else if (repo_config_get_value(r, key, &value) || + color_parse(value, dst)) + strlcpy(dst, default_color, COLOR_MAXLEN); + + free(key); +} + +static int init_add_i_state(struct repository *r, struct add_i_state *s) +{ + const char *value; + + s->r = r; + + if (repo_config_get_value(r, "color.interactive", &value)) + s->use_color = -1; + else + s->use_color = + git_config_colorbool("color.interactive", value); + s->use_color = want_color(s->use_color); + + init_color(r, s, "header", s->header_color, GIT_COLOR_BOLD); + + return 0; +} + struct item { const char *name; }; @@ -14,7 +56,8 @@ struct list_options { void *print_item_data; }; -static void list(struct item **list, size_t nr, struct list_options *opts) +static void list(struct item **list, size_t nr, + struct add_i_state *s, struct list_options *opts) { int i; @@ -22,7 +65,8 @@ static void list(struct item **list, size_t nr, struct list_options *opts) return; if (opts->header) - printf("%s\n", opts->header); + color_fprintf_ln(stdout, s->header_color, + "%s", opts->header); for (i = 0; i < nr; i++) { opts->print_item(i, list[i], opts->print_item_data); @@ -227,16 +271,16 @@ static void print_file_item(int i, struct item *item, printf(" %2d: %s", i + 1, d->buf.buf); } -static int run_status(struct repository *r, const struct pathspec *ps, +static int run_status(struct add_i_state *s, const struct pathspec *ps, struct file_list *files, struct list_options *opts) { reset_file_list(files); - if (get_modified_files(r, files, ps) < 0) + if (get_modified_files(s->r, files, ps) < 0) return -1; if (files->nr) - list((struct item **)files->file, files->nr, opts); + list((struct item **)files->file, files->nr, s, opts); putchar('\n'); return 0; @@ -244,6 +288,7 @@ static int run_status(struct repository *r, const struct pathspec *ps, int run_add_i(struct repository *r, const struct pathspec *ps) { + struct add_i_state s = { NULL }; struct print_file_item_data print_file_item_data = { "%12s %12s %s", STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; @@ -254,13 +299,16 @@ int run_add_i(struct repository *r, const struct pathspec *ps) struct file_list files = { NULL }; int res = 0; + if (init_add_i_state(r, &s)) + return error("could not parse `add -i` config"); + strbuf_addstr(&header, " "); strbuf_addf(&header, print_file_item_data.modified_fmt, _("staged"), _("unstaged"), _("path")); opts.header = header.buf; repo_refresh_and_write_index(r, REFRESH_QUIET, 1); - if (run_status(r, ps, &files, &opts) < 0) + if (run_status(&s, ps, &files, &opts) < 0) res = -1; release_file_list(&files); From patchwork Tue Aug 27 12:57:58 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Arver via GitGitGadget X-Patchwork-Id: 11116879 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6B67213B1 for ; Tue, 27 Aug 2019 12:58:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 3FCF52077B for ; Tue, 27 Aug 2019 12:58:04 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="CZKDDdDP" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729941AbfH0M6D (ORCPT ); Tue, 27 Aug 2019 08:58:03 -0400 Received: from mail-wm1-f68.google.com ([209.85.128.68]:55492 "EHLO mail-wm1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729861AbfH0M6B (ORCPT ); Tue, 27 Aug 2019 08:58:01 -0400 Received: by mail-wm1-f68.google.com with SMTP id f72so2974847wmf.5 for ; Tue, 27 Aug 2019 05:57:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:message-id:in-reply-to:references:from:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=Y4lT1wnaCIJK9Gipd3pEAc/YaEpdH/veIOQxmPJjBGM=; b=CZKDDdDPJ3QmNHFV7JJPMs0ONOCMacS2PX/95QjrgloGxACibTt9RURToxrNVdx6t6 xywvDTTCGTe+66x5/89IfdgKnhAi2fO/EmvxTsDgkquPZZOig2hXoobUMvEp1RBwRlKQ 08cGEC1iEVteqAUwxJTuc0Z5s7GGSLFTbgGi5fIkfMObmGJObQejUxHg4blHXtmgvHuv t4RofcXEJi9rxIfmGwR75W7w9WJko3EfcYIYuAvuD0nf4VQtwMAKvi6wjc6vZhQXdEt+ 0/TFexenSgYyCb1i6VqmL5+HFJNK726t+I3mU/34BOrX36p4MiAkBcvpdeG/i/8Z7Iea uAHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:message-id:in-reply-to:references:from :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=Y4lT1wnaCIJK9Gipd3pEAc/YaEpdH/veIOQxmPJjBGM=; b=dNgCNU8txODbp6S2epDxJYLz2oxCzA0iZ2oB3mYs0FKr6NNYJLz0mN27hgQHKx9gib d7213Y8e22a8MIPTQDhlHUdFr/BeXMKOSKvc6oO+UPWh8Mzt8G6DdFE6jgwKkT4d8RFC PiXZJVvJHibyO1m9CRRRfWqHYM2gaY24SwtrF7LtS7vVQ2tKtE7rk8a6w0+nzBKaZLZ9 1/q3Kf8WTZsiBXMBbXAG+xBnkJt84uZGoap/GKTWRloKatvxdk/Q2KgkWfk74j11VHXA tlEZ7p5ZRgL5JQuinLwKm05wCdgaegEGSCswu1NWCR2WSTyrEkyXrCVfwxCewP8U4bZU x9LQ== X-Gm-Message-State: APjAAAW5EDEURa2IsM9YKJRlIqazpEPZYaTJOc6xK+FA/NaiSDSWNWL+ xDrSu1pJcZ69rK1ahiP8/Sg/LGIV91s= X-Google-Smtp-Source: APXvYqzydwZSkjIKK/FxaHpxTBxKF3bsk1IafnwkfWLFPXDlbGJGjQnOhmDT0izsGKGF70YEzQpSzw== X-Received: by 2002:a1c:f518:: with SMTP id t24mr26962396wmh.98.1566910678537; Tue, 27 Aug 2019 05:57:58 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id d16sm11079940wrv.55.2019.08.27.05.57.58 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 27 Aug 2019 05:57:58 -0700 (PDT) Date: Tue, 27 Aug 2019 05:57:58 -0700 (PDT) X-Google-Original-Date: Tue, 27 Aug 2019 12:57:47 GMT Message-Id: In-Reply-To: References: From: "Johannes Schindelin via GitGitGadget" Subject: [PATCH v4 06/11] built-in add -i: implement the main loop Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: Jeff Hostetler , Jeff King , Junio C Hamano , Johannes Schindelin Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Johannes Schindelin The reason why we did not start with the main loop to begin with is that it is the first user of `list_and_choose()`, which uses the `list()` function that we conveniently introduced for use by the `status` command. Apart from the "and choose" part, there are more differences between the way the `status` command calls the `list_and_choose()` function in the Perl version of `git add -i` compared to the other callers of said function. The most important ones: - The list is not only shown, but the user is also asked to make a choice, possibly selecting multiple entries. - The list of items is prefixed with a marker indicating what items have been selected, if multi-selection is allowed. - Initially, for each item a unique prefix (if there exists any within the given parameters) is determined, and shown in the list, and accepted as a shortcut for the selection. These features will be implemented later, except the part where the user can choose a command. At this stage, though, the built-in `git add -i` still only supports the `status` command, with the remaining commands to follow over the course of the next commits. In addition, we also modify `list()` to support displaying the commands in columns, even if there is currently only one. The Perl script `git-add--interactive.perl` mixed the purposes of the "list" and the "and choose" part into the same function. In the C version, we will keep them separate instead, calling the `list()` function from the `list_and_choose()` function. Note that we only have a prompt ending in a single ">" at this stage; later commits will add commands that display a double ">>" to indicate that the user is in a different loop than the main one. Signed-off-by: Johannes Schindelin --- add-interactive.c | 129 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 2 deletions(-) diff --git a/add-interactive.c b/add-interactive.c index f5577a80d3..bbab69d4bc 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -51,6 +51,7 @@ struct item { }; struct list_options { + int columns; const char *header; void (*print_item)(int i, struct item *item, void *print_item_data); void *print_item_data; @@ -59,7 +60,7 @@ struct list_options { static void list(struct item **list, size_t nr, struct add_i_state *s, struct list_options *opts) { - int i; + int i, last_lf = 0; if (!nr) return; @@ -70,8 +71,97 @@ static void list(struct item **list, size_t nr, for (i = 0; i < nr; i++) { opts->print_item(i, list[i], opts->print_item_data); + + if ((opts->columns) && ((i + 1) % (opts->columns))) { + putchar('\t'); + last_lf = 0; + } + else { + putchar('\n'); + last_lf = 1; + } + } + + if (!last_lf) putchar('\n'); +} +struct list_and_choose_options { + struct list_options list_opts; + + const char *prompt; +}; + +#define LIST_AND_CHOOSE_ERROR (-1) +#define LIST_AND_CHOOSE_QUIT (-2) + +/* + * Returns the selected index. + * + * If an error occurred, returns `LIST_AND_CHOOSE_ERROR`. Upon EOF, + * `LIST_AND_CHOOSE_QUIT` is returned. + */ +static ssize_t list_and_choose(struct item **items, size_t nr, + struct add_i_state *s, + struct list_and_choose_options *opts) +{ + struct strbuf input = STRBUF_INIT; + ssize_t res = LIST_AND_CHOOSE_ERROR; + + for (;;) { + char *p, *endp; + + strbuf_reset(&input); + + list(items, nr, s, &opts->list_opts); + + printf("%s%s", opts->prompt, "> "); + fflush(stdout); + + if (strbuf_getline(&input, stdin) == EOF) { + putchar('\n'); + res = LIST_AND_CHOOSE_QUIT; + break; + } + strbuf_trim(&input); + + if (!input.len) + break; + + p = input.buf; + for (;;) { + size_t sep = strcspn(p, " \t\r\n,"); + ssize_t index = -1; + + if (!sep) { + if (!*p) + break; + p++; + continue; + } + + if (isdigit(*p)) { + index = strtoul(p, &endp, 10) - 1; + if (endp != p + sep) + index = -1; + } + + p[sep] = '\0'; + if (index < 0 || index >= nr) + printf(_("Huh (%s)?\n"), p); + else { + res = index; + break; + } + + p += sep + 1; + } + + if (res != LIST_AND_CHOOSE_ERROR) + break; } + + strbuf_release(&input); + return res; } struct adddel { @@ -286,17 +376,40 @@ static int run_status(struct add_i_state *s, const struct pathspec *ps, return 0; } +static void print_command_item(int i, struct item *item, + void *print_command_item_data) +{ + printf(" %2d: %s", i + 1, item->name); +} + +struct command_item { + struct item item; + int (*command)(struct add_i_state *s, const struct pathspec *ps, + struct file_list *files, struct list_options *opts); +}; + int run_add_i(struct repository *r, const struct pathspec *ps) { struct add_i_state s = { NULL }; + struct list_and_choose_options main_loop_opts = { + { 4, N_("*** Commands ***"), print_command_item, NULL }, + N_("What now") + }; + struct command_item + status = { { "status" }, run_status }; + struct command_item *commands[] = { + &status + }; + struct print_file_item_data print_file_item_data = { "%12s %12s %s", STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; struct list_options opts = { - NULL, print_file_item, &print_file_item_data + 0, NULL, print_file_item, &print_file_item_data }; struct strbuf header = STRBUF_INIT; struct file_list files = { NULL }; + ssize_t i; int res = 0; if (init_add_i_state(r, &s)) @@ -311,6 +424,18 @@ int run_add_i(struct repository *r, const struct pathspec *ps) if (run_status(&s, ps, &files, &opts) < 0) res = -1; + for (;;) { + i = list_and_choose((struct item **)commands, + ARRAY_SIZE(commands), &s, &main_loop_opts); + if (i == LIST_AND_CHOOSE_QUIT) { + printf(_("Bye.\n")); + res = 0; + break; + } + if (i != LIST_AND_CHOOSE_ERROR) + res = commands[i]->command(&s, ps, &files, &opts); + } + release_file_list(&files); strbuf_release(&print_file_item_data.buf); strbuf_release(&print_file_item_data.index); From patchwork Tue Aug 27 12:57:58 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Arver via GitGitGadget X-Patchwork-Id: 11116891 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 289A6174A for ; Tue, 27 Aug 2019 12:58:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id E6C68206BF for ; Tue, 27 Aug 2019 12:58:11 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="jsceBp3D" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728188AbfH0M6L (ORCPT ); Tue, 27 Aug 2019 08:58:11 -0400 Received: from mail-wr1-f66.google.com ([209.85.221.66]:37862 "EHLO mail-wr1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729898AbfH0M6D (ORCPT ); Tue, 27 Aug 2019 08:58:03 -0400 Received: by mail-wr1-f66.google.com with SMTP id z11so18719477wrt.4 for ; Tue, 27 Aug 2019 05:58:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:message-id:in-reply-to:references:from:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=3EMC294rIyauU6ygUTkuPxckieWcv6bbZs3Yi/71pnQ=; b=jsceBp3DwPOAH9F3mg82MwcAa4YyQaI7fmZsruilYhqSAAzOvH2za0B4ZgwwdvCahX RH44qqH7ppWJcIM0ZAR2O9cWWnLxBItllhCUbIzkUIza+If0d/Zz8Z86cIkJNNMHFDIB VhYAYs4Cs5v+nMUaELW7taao15o7q9QHY2y1CY8/FwgpDrBzmeuqNz4T9I8HCEVbiPXp NgsEvRTYZDwyopikXxywH4I5SWtm3VKzRAldFbgxX+I2p4NKfBepBibqgVEGKiR4tF2u 0CCz2v3YvZFZmJRUMZ9Zonrk5UESk6xVya7C7/QEUqm6FtAf3eAVTfRSFps9PZHE8dzS pnfA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:message-id:in-reply-to:references:from :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=3EMC294rIyauU6ygUTkuPxckieWcv6bbZs3Yi/71pnQ=; b=JNtBS0+P5bmTKi8wuCnHs39uERmlnl/t1B1tIoac1GYW0/dxKxmUT3OHMYT0AfiiSp e0qBzjoOypsbDR1CwkFc9Xny4CmCc83eyLtJWp3Z6h9+V3lRNcmhRii6JTKu/JJosPnI g2rdZ/aeMuRo0PxqD3SwIa4oYtwkZGZGe6RqO4U/hLsko8eoEJcCtlwWj38DUopkoGVZ wv6j3Rc/3MX/HWbOVk3Bm5FLVEGKOY+Xq9Qcs6Jn3bmg58RU3Tex7LqB8clMTeZSyIJc pDDC2qZAmNQodX4NPhc+np9qilLwAPYforcIlQFG9c2887wfFKgZhs8xCNJuag/IX7kF JkBw== X-Gm-Message-State: APjAAAVYc/T8pkElCAL+BP/pHh3qD58xT4BjFZmaZ/Gl4cjSD21aqjH5 Iwf4jLuLaaXTf9qp/W80aXjtlEedX4U= X-Google-Smtp-Source: APXvYqwRpWmI40tPHJygCi6vjnB+iI79Qv5QdCz76CkrD3LuUUY+lWDN9R/7zUyHy+MR3hd4R3wbNQ== X-Received: by 2002:a5d:604d:: with SMTP id j13mr28379796wrt.244.1566910679234; Tue, 27 Aug 2019 05:57:59 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id t14sm14300192wrv.12.2019.08.27.05.57.58 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 27 Aug 2019 05:57:58 -0700 (PDT) Date: Tue, 27 Aug 2019 05:57:58 -0700 (PDT) X-Google-Original-Date: Tue, 27 Aug 2019 12:57:48 GMT Message-Id: In-Reply-To: References: From: "Slavica Djukic via GitGitGadget" Subject: [PATCH v4 07/11] Add a function to determine unique prefixes for a list of strings Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: Jeff Hostetler , Jeff King , Junio C Hamano , Slavica Djukic Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Slavica Djukic In the `git add -i` command, we show unique prefixes of the commands and files, to give an indication what prefix would select them. Naturally, the C implementation looks a lot different than the Perl implementation: in Perl, a trie is much easier implemented, while we already have a pretty neat hashmap implementation in C that we use for the purpose of storing (not necessarily unique) prefixes. The idea: for each item that we add, we generate prefixes starting with the first letter, then the first two letters, then three, etc, until we find a prefix that is unique (or until the prefix length would be longer than we want). If we encounter a previously-unique prefix on the way, we adjust that item's prefix to make it unique again (or we mark it as having no unique prefix if we failed to find one). These partial prefixes are stored in a hash map (for quick lookup times). To make sure that this function works as expected, we add a test using a special-purpose test helper. Note: We expect the list of prefix items to be passed in as a list of pointers rather than as regular list to avoid having to copy information (the actual items will most likely contain more information than just the name and the length of the unique prefix, but passing in `struct prefix_item *` would not allow for that). Signed-off-by: Slavica Djukic Signed-off-by: Johannes Schindelin --- Makefile | 2 + prefix-map.c | 123 +++++++++++++++++++++++++++++++++++++ prefix-map.h | 29 +++++++++ t/helper/test-prefix-map.c | 58 +++++++++++++++++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + t/t0018-prefix-map.sh | 10 +++ 7 files changed, 224 insertions(+) create mode 100644 prefix-map.c create mode 100644 prefix-map.h create mode 100644 t/helper/test-prefix-map.c create mode 100755 t/t0018-prefix-map.sh diff --git a/Makefile b/Makefile index d04daf9fd8..5c141c4609 100644 --- a/Makefile +++ b/Makefile @@ -727,6 +727,7 @@ TEST_BUILTINS_OBJS += test-online-cpus.o TEST_BUILTINS_OBJS += test-parse-options.o TEST_BUILTINS_OBJS += test-path-utils.o TEST_BUILTINS_OBJS += test-pkt-line.o +TEST_BUILTINS_OBJS += test-prefix-map.o TEST_BUILTINS_OBJS += test-prio-queue.o TEST_BUILTINS_OBJS += test-reach.o TEST_BUILTINS_OBJS += test-read-cache.o @@ -945,6 +946,7 @@ LIB_OBJS += patch-ids.o LIB_OBJS += path.o LIB_OBJS += pathspec.o LIB_OBJS += pkt-line.o +LIB_OBJS += prefix-map.o LIB_OBJS += preload-index.o LIB_OBJS += pretty.o LIB_OBJS += prio-queue.o diff --git a/prefix-map.c b/prefix-map.c new file mode 100644 index 0000000000..ff4284bbe3 --- /dev/null +++ b/prefix-map.c @@ -0,0 +1,123 @@ +#include "cache.h" +#include "prefix-map.h" + +struct prefix_map_entry { + struct hashmap_entry e; + const char *name; + size_t prefix_length; + /* if item is NULL, the prefix is not unique */ + struct prefix_item *item; +}; + +struct prefix_map { + struct hashmap map; + size_t min_length, max_length; +}; + +static int map_cmp(const void *unused_cmp_data, + const void *entry, + const void *entry_or_key, + const void *unused_keydata) +{ + const struct prefix_map_entry *a = entry; + const struct prefix_map_entry *b = entry_or_key; + + return a->prefix_length != b->prefix_length || + strncmp(a->name, b->name, a->prefix_length); +} + +static void add_prefix_entry(struct hashmap *map, const char *name, + size_t prefix_length, struct prefix_item *item) +{ + struct prefix_map_entry *result = xmalloc(sizeof(*result)); + result->name = name; + result->prefix_length = prefix_length; + result->item = item; + hashmap_entry_init(&result->e, memhash(name, prefix_length)); + hashmap_add(map, &result->e); +} + +static void init_prefix_map(struct prefix_map *prefix_map, + size_t min_prefix_length, size_t max_prefix_length) +{ + hashmap_init(&prefix_map->map, map_cmp, NULL, 0); + prefix_map->min_length = min_prefix_length; + prefix_map->max_length = max_prefix_length; +} + +static void add_prefix_item(struct prefix_map *prefix_map, + struct prefix_item *item) +{ + struct prefix_map_entry e = { { NULL } }, *e2; + size_t j; + + e.item = item; + e.name = item->name; + + for (j = prefix_map->min_length; + j <= prefix_map->max_length && e.name[j]; j++) { + /* Avoid breaking UTF-8 multi-byte sequences */ + if (!isascii(e.name[j])) + break; + + e.prefix_length = j; + hashmap_entry_init(&e.e, memhash(e.name, j)); + e2 = hashmap_get(&prefix_map->map, &e.e, NULL); + if (!e2) { + /* prefix is unique at this stage */ + item->prefix_length = j; + add_prefix_entry(&prefix_map->map, e.name, j, item); + break; + } + + if (!e2->item) + continue; /* non-unique prefix */ + + if (j != e2->item->prefix_length || memcmp(e.name, e2->name, j)) + BUG("unexpected prefix length: %d != %d (%s != %s)", + (int)j, (int)e2->item->prefix_length, + e.name, e2->name); + + /* skip common prefix */ + for (; j < prefix_map->max_length && e.name[j]; j++) { + if (e.item->name[j] != e2->item->name[j]) + break; + add_prefix_entry(&prefix_map->map, e.name, j + 1, + NULL); + } + + /* e2 no longer refers to a unique prefix */ + if (j < prefix_map->max_length && e2->name[j]) { + /* found a new unique prefix for e2's item */ + e2->item->prefix_length = j + 1; + add_prefix_entry(&prefix_map->map, e2->name, j + 1, + e2->item); + } + else + e2->item->prefix_length = 0; + e2->item = NULL; + + if (j < prefix_map->max_length && e.name[j]) { + /* found a unique prefix for the item */ + e.item->prefix_length = j + 1; + add_prefix_entry(&prefix_map->map, e.name, j + 1, + e.item); + } else + /* item has no (short enough) unique prefix */ + e.item->prefix_length = 0; + + break; + } +} + +void find_unique_prefixes(struct prefix_item **array, size_t nr, + size_t min_length, size_t max_length) +{ + size_t i; + struct prefix_map prefix_map; + + init_prefix_map(&prefix_map, min_length, max_length); + for (i = 0; i < nr; i++) + add_prefix_item(&prefix_map, array[i]); + hashmap_free(&prefix_map.map, 1); +} diff --git a/prefix-map.h b/prefix-map.h new file mode 100644 index 0000000000..0c55ee0b2b --- /dev/null +++ b/prefix-map.h @@ -0,0 +1,29 @@ +#ifndef PREFIX_MAP_H +#define PREFIX_MAP_H + +#include "hashmap.h" + +struct prefix_item { + const char *name; + size_t prefix_length; +}; + +/* + * Given an array of names, find unique prefixes (i.e. the first characters + * that uniquely identify the names) and store the lengths of the unique + * prefixes in the 'prefix_length' field of the elements of the given array.. + * + * Typically, the `struct prefix_item` information is a field in the actual + * item struct; For this reason, the `array` parameter is specified as an array + * of pointers to the items. + * + * The `min_length`/`max_length` parameters define what length the unique + * prefixes should have. + * + * If no unique prefix could be found for a given item, its `prefix_length` + * will be set to 0. + */ +void find_unique_prefixes(struct prefix_item **array, size_t nr, + size_t min_length, size_t max_length); + +#endif diff --git a/t/helper/test-prefix-map.c b/t/helper/test-prefix-map.c new file mode 100644 index 0000000000..3f1c90eaf0 --- /dev/null +++ b/t/helper/test-prefix-map.c @@ -0,0 +1,58 @@ +#include "test-tool.h" +#include "cache.h" +#include "prefix-map.h" + +static size_t test_count, failed_count; + +static void check(int succeeded, const char *file, size_t line_no, + const char *fmt, ...) +{ + va_list ap; + + test_count++; + if (succeeded) + return; + + va_start(ap, fmt); + fprintf(stderr, "%s:%d: ", file, (int)line_no); + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); + va_end(ap); + + failed_count++; +} + +#define EXPECT_SIZE_T_EQUALS(expect, actual, hint) \ + check(expect == actual, __FILE__, __LINE__, \ + "size_t's do not match: %" \ + PRIdMAX " != %" PRIdMAX " (%s) (%s)", \ + (intmax_t)expect, (intmax_t)actual, #actual, hint) + +int cmd__prefix_map(int argc, const char **argv) +{ +#define NR 5 + struct prefix_item items[NR] = { + { "unique" }, + { "hell" }, + { "hello" }, + { "wok" }, + { "world" }, + }; + struct prefix_item *list[NR] = { + items, items + 1, items + 2, items + 3, items + 4 + }; + + find_unique_prefixes(list, NR, 1, 3); + +#define EXPECT_PREFIX_LENGTH_EQUALS(expect, index) \ + EXPECT_SIZE_T_EQUALS(expect, list[index]->prefix_length, \ + list[index]->name) + + EXPECT_PREFIX_LENGTH_EQUALS(1, 0); + EXPECT_PREFIX_LENGTH_EQUALS(0, 1); + EXPECT_PREFIX_LENGTH_EQUALS(0, 2); + EXPECT_PREFIX_LENGTH_EQUALS(3, 3); + EXPECT_PREFIX_LENGTH_EQUALS(3, 4); + + return !!failed_count; +} diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index ce7e89028c..34ddf3f8b7 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -41,6 +41,7 @@ static struct test_cmd cmds[] = { { "parse-options", cmd__parse_options }, { "path-utils", cmd__path_utils }, { "pkt-line", cmd__pkt_line }, + { "prefix-map", cmd__prefix_map }, { "prio-queue", cmd__prio_queue }, { "reach", cmd__reach }, { "read-cache", cmd__read_cache }, diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index f805bb39ae..400854e60d 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -31,6 +31,7 @@ int cmd__online_cpus(int argc, const char **argv); int cmd__parse_options(int argc, const char **argv); int cmd__path_utils(int argc, const char **argv); int cmd__pkt_line(int argc, const char **argv); +int cmd__prefix_map(int argc, const char **argv); int cmd__prio_queue(int argc, const char **argv); int cmd__reach(int argc, const char **argv); int cmd__read_cache(int argc, const char **argv); diff --git a/t/t0018-prefix-map.sh b/t/t0018-prefix-map.sh new file mode 100755 index 0000000000..187fa92aec --- /dev/null +++ b/t/t0018-prefix-map.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +test_description='basic tests for prefix map' +. ./test-lib.sh + +test_expect_success 'prefix map' ' + test-tool prefix-map +' + +test_done From patchwork Tue Aug 27 12:57:59 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Arver via GitGitGadget X-Patchwork-Id: 11116875 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3C68813B1 for ; Tue, 27 Aug 2019 12:58:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 102BF2077B for ; Tue, 27 Aug 2019 12:58:03 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="sEH31cd6" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729925AbfH0M6C (ORCPT ); Tue, 27 Aug 2019 08:58:02 -0400 Received: from mail-wr1-f66.google.com ([209.85.221.66]:40301 "EHLO mail-wr1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729901AbfH0M6B (ORCPT ); Tue, 27 Aug 2019 08:58:01 -0400 Received: by mail-wr1-f66.google.com with SMTP id c3so18687853wrd.7 for ; Tue, 27 Aug 2019 05:58:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:message-id:in-reply-to:references:from:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=Z9ZC6ebRefB3vGsu5gtP8HC6qsK2bvviQz2Ah4aJVyY=; b=sEH31cd6gte4dnncsceD7IFLJyZsVsV5Pvdz3MpUP/dFhp8c/+UovTmfWHGmg6BnGa 7tuqj4RakkR0Uy1TfLX2PKQ3dJLzgQtKFLuMBtQVEujrnpc5rq6a/HkB8Nz3MS1hrjwt FrMPjiAkB1Z4l3nppNasjoJM9egDwAhG8iKeov9GQr7u/7BiZm7VtCmhi/XH7k/UTlvn 3/HM2wBW4qJLajo3JvpB+AfSmF36fevPmzqf87Ds1M/pnn3rdxNJJyhcJhFNGPFbnF3P mNJtUvNZefmNNrQ1R0fcm9G09U6XNb+B+aZMEMFmvqXcSArhxXTH0oCEgGk6c19i+8/z dbQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:message-id:in-reply-to:references:from :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=Z9ZC6ebRefB3vGsu5gtP8HC6qsK2bvviQz2Ah4aJVyY=; b=lgsQR36AG+iJDX3ELJNUFI23ITEJNXWtjYJJu1mvrg5sSuQAxlywLOq7awKEh0O5Ri DXQkQRU1InfrMx05WrbrKqOr8ae1Wv/8cwqbcHjmz18azYOwMFNcgls0Pez9maYyMHot z9/DNm+xMVbPdiEtINKGjWx6RiwgvTa+IjagNUJZHUvx4Rc+eygtfdpfP/D3fjMVqkzs +eiE96br18U7PDsNPN4YbCBnDkl/B6gteBOc2VdK8fgTrfBEw+VAXbAj46PyzFe4oSUv 5ZknOTuI9BtMQiQhPCHQoH2PiagAFPXF1SzgXwgM9g0JVYXUXBbwvOEPS3hcL15SE6Un uIUA== X-Gm-Message-State: APjAAAXKWonUF1oPySzEcdb2Ob0SixbSyeTZiZekIyKh3cLGeIW/b3ne BxI8VTQ5T98YWb04fbaLpqkVtFP6JWY= X-Google-Smtp-Source: APXvYqzPk7zW2fZFHds4S1EZboKLtI/st1ooaRcMfl/S+TDqyU0UP8NlINN/q/a8MPgfnSib7Ant/A== X-Received: by 2002:adf:f1cc:: with SMTP id z12mr28674496wro.125.1566910680107; Tue, 27 Aug 2019 05:58:00 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id w13sm40217521wre.44.2019.08.27.05.57.59 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 27 Aug 2019 05:57:59 -0700 (PDT) Date: Tue, 27 Aug 2019 05:57:59 -0700 (PDT) X-Google-Original-Date: Tue, 27 Aug 2019 12:57:49 GMT Message-Id: In-Reply-To: References: From: "Slavica Djukic via GitGitGadget" Subject: [PATCH v4 08/11] built-in add -i: show unique prefixes of the commands Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: Jeff Hostetler , Jeff King , Junio C Hamano , Slavica Djukic Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Slavica Djukic Just like in the Perl script `git-add--interactive.perl`, for each command a unique prefix is determined (if there exists any within the given parameters), and shown in the list, and accepted as a shortcut for the command. We use the prefix map implementation that we just added in the previous commit for that purpose. Signed-off-by: Slavica Djukic Signed-off-by: Johannes Schindelin --- add-interactive.c | 69 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/add-interactive.c b/add-interactive.c index bbab69d4bc..12a4c2adb8 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -5,6 +5,7 @@ #include "diffcore.h" #include "revision.h" #include "refs.h" +#include "prefix-map.h" struct add_i_state { struct repository *r; @@ -46,18 +47,32 @@ static int init_add_i_state(struct repository *r, struct add_i_state *s) return 0; } -struct item { - const char *name; -}; +static ssize_t find_unique(const char *string, + struct prefix_item **list, size_t nr) +{ + ssize_t found = -1, i; + + for (i = 0; i < nr; i++) { + struct prefix_item *item = list[i]; + if (!starts_with(item->name, string)) + continue; + if (found >= 0) + return -1; + found = i; + } + + return found; +} struct list_options { int columns; const char *header; - void (*print_item)(int i, struct item *item, void *print_item_data); + void (*print_item)(int i, struct prefix_item *item, + void *print_item_data); void *print_item_data; }; -static void list(struct item **list, size_t nr, +static void list(struct prefix_item **list, size_t nr, struct add_i_state *s, struct list_options *opts) { int i, last_lf = 0; @@ -100,13 +115,15 @@ struct list_and_choose_options { * If an error occurred, returns `LIST_AND_CHOOSE_ERROR`. Upon EOF, * `LIST_AND_CHOOSE_QUIT` is returned. */ -static ssize_t list_and_choose(struct item **items, size_t nr, +static ssize_t list_and_choose(struct prefix_item **items, size_t nr, struct add_i_state *s, struct list_and_choose_options *opts) { struct strbuf input = STRBUF_INIT; ssize_t res = LIST_AND_CHOOSE_ERROR; + find_unique_prefixes(items, nr, 1, 4); + for (;;) { char *p, *endp; @@ -146,6 +163,9 @@ static ssize_t list_and_choose(struct item **items, size_t nr, } p[sep] = '\0'; + if (index < 0) + index = find_unique(p, items, nr); + if (index < 0 || index >= nr) printf(_("Huh (%s)?\n"), p); else { @@ -171,7 +191,7 @@ struct adddel { struct file_list { struct file_item { - struct item item; + struct prefix_item item; struct adddel index, worktree; } **file; size_t nr, alloc; @@ -338,12 +358,29 @@ static void populate_wi_changes(struct strbuf *buf, strbuf_addstr(buf, no_changes); } +/* filters out prefixes which have special meaning to list_and_choose() */ +static int is_valid_prefix(const char *prefix, size_t prefix_len) +{ + return prefix_len && prefix && + /* + * We expect `prefix` to be NUL terminated, therefore this + * `strcspn()` call is okay, even if it might do much more + * work than strictly necessary. + */ + strcspn(prefix, " \t\r\n,") >= prefix_len && /* separators */ + *prefix != '-' && /* deselection */ + !isdigit(*prefix) && /* selection */ + (prefix_len != 1 || + (*prefix != '*' && /* "all" wildcard */ + *prefix != '?')); /* prompt help */ +} + struct print_file_item_data { const char *modified_fmt; struct strbuf buf, index, worktree; }; -static void print_file_item(int i, struct item *item, +static void print_file_item(int i, struct prefix_item *item, void *print_file_item_data) { struct file_item *c = (struct file_item *)item; @@ -370,20 +407,26 @@ static int run_status(struct add_i_state *s, const struct pathspec *ps, return -1; if (files->nr) - list((struct item **)files->file, files->nr, s, opts); + list((struct prefix_item **)files->file, files->nr, s, opts); putchar('\n'); return 0; } -static void print_command_item(int i, struct item *item, +static void print_command_item(int i, struct prefix_item *item, void *print_command_item_data) { - printf(" %2d: %s", i + 1, item->name); + if (!item->prefix_length || + !is_valid_prefix(item->name, item->prefix_length)) + printf(" %2d: %s", i + 1, item->name); + else + printf(" %3d: [%.*s]%s", i + 1, + (int)item->prefix_length, item->name, + item->name + item->prefix_length); } struct command_item { - struct item item; + struct prefix_item item; int (*command)(struct add_i_state *s, const struct pathspec *ps, struct file_list *files, struct list_options *opts); }; @@ -425,7 +468,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps) res = -1; for (;;) { - i = list_and_choose((struct item **)commands, + i = list_and_choose((struct prefix_item **)commands, ARRAY_SIZE(commands), &s, &main_loop_opts); if (i == LIST_AND_CHOOSE_QUIT) { printf(_("Bye.\n")); From patchwork Tue Aug 27 12:58:00 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Arver via GitGitGadget X-Patchwork-Id: 11116883 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 781D413B1 for ; Tue, 27 Aug 2019 12:58:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 5687D2077B for ; Tue, 27 Aug 2019 12:58:06 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="itE+/t4x" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729960AbfH0M6F (ORCPT ); Tue, 27 Aug 2019 08:58:05 -0400 Received: from mail-wm1-f65.google.com ([209.85.128.65]:39296 "EHLO mail-wm1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729916AbfH0M6C (ORCPT ); Tue, 27 Aug 2019 08:58:02 -0400 Received: by mail-wm1-f65.google.com with SMTP id i63so2915852wmg.4 for ; Tue, 27 Aug 2019 05:58:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:message-id:in-reply-to:references:from:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=4lHPngCpOBZoIfXJc8hQeYD3AiiGaRcngpUiY0erUUI=; b=itE+/t4xNugjJ92t0Q/hF4HodcFfbO8CH0Ej0sLS1RQbv+6v7cQIfF3Hl7OgUgJdB7 quJ9so0/TRRGYe166kqWRn5Og+7yEP8EvSqA2OA2iiFTLP5dmU0/MvocISW5Zb+m8uG4 T64OPfoxIySQ9jTazNUHr+c79J8Y9//kRQa4+pTm6udiJnzpOq11F3Zsf+pFygfOehny 5MJKB0Oi6RlPL3BLwCtzqkgmkG80hXNBMbHuPb8ExoyodWcPACEJDjuoL8bYAFbGiucI aS6RyXzI+CUPjctTU/KpfSrMP+9otHHS01anxQxz6o0lUNVuaOfXR/6uACe0biC3Os21 lEKA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:message-id:in-reply-to:references:from :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=4lHPngCpOBZoIfXJc8hQeYD3AiiGaRcngpUiY0erUUI=; b=LPjboasFB5L4Q50chLqGSoEjOeZd8MifX6Yl17tp51/d3Y3fQMd2hQUgeQo1Z5ASe1 bp+SQQBMtq7VExJX/hR221SL5vZkWy20LcwMheGoKlRN49djxE59Z0YUJwHc7G5kviVu Dohn7Wvit6r6U1TmiQdX40LUG4QfX+wglJ2FQodXfbUry8zyZn/SDm7pAMJWlBh5BVgF /xDqlpf7uQ6fFywwr8PgI0CUvP1Ys1CoL+2B1ZPuBPjrErXFuzzO7Df2OWxDfMMZqOgI OfcmvL67aR45tMAxNh7wntnYjehBJKPq0KOV8d5+LloYdGPdOUyu34raBJYnZyShILto t2ag== X-Gm-Message-State: APjAAAVYuavxYtXWdBWPQVVEcQSmlrerymZ409MzbCLQISdP4pyGH3Uj QTEESLALWXtkW9hP9Uot/UBINFq1wQc= X-Google-Smtp-Source: APXvYqyjsFYIkqWKXMxtyxK5rwO1bFFURauoXheL8wBKcodFPdZIRHSpaJO7ATQCHmU0v8GrQqlnuQ== X-Received: by 2002:a1c:356:: with SMTP id 83mr29306998wmd.40.1566910680897; Tue, 27 Aug 2019 05:58:00 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id e14sm2272436wme.35.2019.08.27.05.58.00 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 27 Aug 2019 05:58:00 -0700 (PDT) Date: Tue, 27 Aug 2019 05:58:00 -0700 (PDT) X-Google-Original-Date: Tue, 27 Aug 2019 12:57:50 GMT Message-Id: <74f73e26b4de4bb7873079811d1251a938c58522.1566910672.git.gitgitgadget@gmail.com> In-Reply-To: References: From: "Johannes Schindelin via GitGitGadget" Subject: [PATCH v4 09/11] built-in add -i: support `?` (prompt help) Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: Jeff Hostetler , Jeff King , Junio C Hamano , Johannes Schindelin Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Johannes Schindelin With this change, we print out the same colored help text that the Perl-based `git add -i` prints in the main loop when question mark is entered. Signed-off-by: Johannes Schindelin --- add-interactive.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/add-interactive.c b/add-interactive.c index 12a4c2adb8..710317e599 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -11,6 +11,7 @@ struct add_i_state { struct repository *r; int use_color; char header_color[COLOR_MAXLEN]; + char help_color[COLOR_MAXLEN]; }; static void init_color(struct repository *r, struct add_i_state *s, @@ -43,6 +44,7 @@ static int init_add_i_state(struct repository *r, struct add_i_state *s) s->use_color = want_color(s->use_color); init_color(r, s, "header", s->header_color, GIT_COLOR_BOLD); + init_color(r, s, "help", s->help_color, GIT_COLOR_BOLD_RED); return 0; } @@ -104,6 +106,7 @@ struct list_and_choose_options { struct list_options list_opts; const char *prompt; + void (*print_help)(struct add_i_state *s); }; #define LIST_AND_CHOOSE_ERROR (-1) @@ -144,6 +147,11 @@ static ssize_t list_and_choose(struct prefix_item **items, size_t nr, if (!input.len) break; + if (!strcmp(input.buf, "?")) { + opts->print_help(s); + continue; + } + p = input.buf; for (;;) { size_t sep = strcspn(p, " \t\r\n,"); @@ -431,12 +439,24 @@ struct command_item { struct file_list *files, struct list_options *opts); }; +static void command_prompt_help(struct add_i_state *s) +{ + const char *help_color = s->help_color; + color_fprintf_ln(stdout, help_color, "%s", _("Prompt help:")); + color_fprintf_ln(stdout, help_color, "1 - %s", + _("select a numbered item")); + color_fprintf_ln(stdout, help_color, "foo - %s", + _("select item based on unique prefix")); + color_fprintf_ln(stdout, help_color, " - %s", + _("(empty) select nothing")); +} + int run_add_i(struct repository *r, const struct pathspec *ps) { struct add_i_state s = { NULL }; struct list_and_choose_options main_loop_opts = { { 4, N_("*** Commands ***"), print_command_item, NULL }, - N_("What now") + N_("What now"), command_prompt_help }; struct command_item status = { { "status" }, run_status }; From patchwork Tue Aug 27 12:58:01 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Arver via GitGitGadget X-Patchwork-Id: 11116887 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id ABE7113B1 for ; Tue, 27 Aug 2019 12:58:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7E7D7206BF for ; Tue, 27 Aug 2019 12:58:08 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ZgSQyV0g" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729971AbfH0M6G (ORCPT ); Tue, 27 Aug 2019 08:58:06 -0400 Received: from mail-wm1-f67.google.com ([209.85.128.67]:54491 "EHLO mail-wm1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726278AbfH0M6E (ORCPT ); Tue, 27 Aug 2019 08:58:04 -0400 Received: by mail-wm1-f67.google.com with SMTP id p74so2979987wme.4 for ; Tue, 27 Aug 2019 05:58:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:message-id:in-reply-to:references:from:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=6ED/rVKnKuuq7jmLirz2DCfUlaGHF0yH6WNO+b8BtLo=; b=ZgSQyV0gh+dS4ZD4L/10VUSgjgnjluVIVgSgioXNFNUOiHe5U5Dz18TlpUAU+nfHqD WNA+8NjX2DGqSiMPmCzgVQMjwTdWRv0Jt/W4xYJxkMWex4gAV2deWb3Be6uuuBr0c8eB ilBjBEgGqFedyFxvVG0KJTkDjkNsWujc0uCnG0i7plsjRq8kWxIe4PmbRUh8kFuDPFvn OPoFqdnTuwv+JN8k/GeL3sYXhTvBUGkXlk6p6R2JAilCO4Kvxtnft6stlDmIjXJCpdVI +Dua1TYmp2QLavK7IbhWplEOEvTdPJMqYt8E5NV1bw6NP4Zjvy/C7YoD2qLkL3USPWTd 0yRQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:message-id:in-reply-to:references:from :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=6ED/rVKnKuuq7jmLirz2DCfUlaGHF0yH6WNO+b8BtLo=; b=Bvo1x5KQj9HO5qkzMIky7WY5odHOCxWrM8StFKuPcGT1BWmLF0sOqpHEWF5a6k557m DxdBr8KtIuG2MucnCUX7Yk/zAz1ZVbn1BHt70E7eO8AJs8/LCixt9uWzQIMRgsvgZ+NW 9ubgwP2stQ187R4QLrz3kg6zdRveFptX1pjAwjdnS9vlbZvSEj7pnzS7NPujVY/c84Q+ g5Ao0dtyRI9DnbJsPwFcTlUHyhzzwFabpd1QREPkYyxs3z3KPZnLxX/CKEUe0xfyG+1n H4JPttQPjxaGa31abnDx7FyMllmnBUyUlPy9Hm/7T8H8CHGQmmD7bNmy898BGlipCvmA QHxQ== X-Gm-Message-State: APjAAAXwwGgUdRDado7rLGbmI/nQ3lNZvjF9zXeNByvnlXLEWZN/DzXD jri80Evai8VlOMvFo+kJxabqXe0PY8w= X-Google-Smtp-Source: APXvYqwiJ2iPCy1b0A1GfI7JYtZ95zEnOWLXW/SkKUcnheLh0g8m5h6MMm4vjRIX7ZgS0H3YdUEvXQ== X-Received: by 2002:a1c:7dc8:: with SMTP id y191mr29048725wmc.78.1566910681582; Tue, 27 Aug 2019 05:58:01 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id n12sm2216847wmc.24.2019.08.27.05.58.01 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 27 Aug 2019 05:58:01 -0700 (PDT) Date: Tue, 27 Aug 2019 05:58:01 -0700 (PDT) X-Google-Original-Date: Tue, 27 Aug 2019 12:57:51 GMT Message-Id: <88001009bc00b10856141cb689d31400a68b7df3.1566910672.git.gitgitgadget@gmail.com> In-Reply-To: References: From: "Slavica Djukic via GitGitGadget" Subject: [PATCH v4 10/11] built-in add -i: use color in the main loop Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: Jeff Hostetler , Jeff King , Junio C Hamano , Slavica Djukic Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Slavica Djukic The error messages as well as the unique prefixes are colored in `git add -i` by default; We need to do the same in the built-in version. Signed-off-by: Slavica Djukic Signed-off-by: Johannes Schindelin --- add-interactive.c | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/add-interactive.c b/add-interactive.c index 710317e599..a343195a67 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -12,6 +12,9 @@ struct add_i_state { int use_color; char header_color[COLOR_MAXLEN]; char help_color[COLOR_MAXLEN]; + char prompt_color[COLOR_MAXLEN]; + char error_color[COLOR_MAXLEN]; + char reset_color[COLOR_MAXLEN]; }; static void init_color(struct repository *r, struct add_i_state *s, @@ -45,6 +48,9 @@ static int init_add_i_state(struct repository *r, struct add_i_state *s) init_color(r, s, "header", s->header_color, GIT_COLOR_BOLD); init_color(r, s, "help", s->help_color, GIT_COLOR_BOLD_RED); + init_color(r, s, "prompt", s->prompt_color, GIT_COLOR_BOLD_BLUE); + init_color(r, s, "error", s->error_color, GIT_COLOR_BOLD_RED); + init_color(r, s, "reset", s->reset_color, GIT_COLOR_RESET); return 0; } @@ -134,7 +140,8 @@ static ssize_t list_and_choose(struct prefix_item **items, size_t nr, list(items, nr, s, &opts->list_opts); - printf("%s%s", opts->prompt, "> "); + color_fprintf(stdout, s->prompt_color, "%s", opts->prompt); + fputs("> ", stdout); fflush(stdout); if (strbuf_getline(&input, stdin) == EOF) { @@ -175,7 +182,8 @@ static ssize_t list_and_choose(struct prefix_item **items, size_t nr, index = find_unique(p, items, nr); if (index < 0 || index >= nr) - printf(_("Huh (%s)?\n"), p); + color_fprintf_ln(stdout, s->error_color, + _("Huh (%s)?"), p); else { res = index; break; @@ -421,15 +429,21 @@ static int run_status(struct add_i_state *s, const struct pathspec *ps, return 0; } +struct print_command_item_data { + const char *color, *reset; +}; + static void print_command_item(int i, struct prefix_item *item, void *print_command_item_data) { + struct print_command_item_data *d = print_command_item_data; + if (!item->prefix_length || !is_valid_prefix(item->name, item->prefix_length)) printf(" %2d: %s", i + 1, item->name); else - printf(" %3d: [%.*s]%s", i + 1, - (int)item->prefix_length, item->name, + printf(" %2d: %s%.*s%s%s", i + 1, + d->color, (int)item->prefix_length, item->name, d->reset, item->name + item->prefix_length); } @@ -454,8 +468,9 @@ static void command_prompt_help(struct add_i_state *s) int run_add_i(struct repository *r, const struct pathspec *ps) { struct add_i_state s = { NULL }; + struct print_command_item_data data; struct list_and_choose_options main_loop_opts = { - { 4, N_("*** Commands ***"), print_command_item, NULL }, + { 4, N_("*** Commands ***"), print_command_item, &data }, N_("What now"), command_prompt_help }; struct command_item @@ -478,6 +493,18 @@ int run_add_i(struct repository *r, const struct pathspec *ps) if (init_add_i_state(r, &s)) return error("could not parse `add -i` config"); + /* + * When color was asked for, use the prompt color for + * highlighting, otherwise use square brackets. + */ + if (s.use_color) { + data.color = s.prompt_color; + data.reset = s.reset_color; + } else { + data.color = "["; + data.reset = "]"; + } + strbuf_addstr(&header, " "); strbuf_addf(&header, print_file_item_data.modified_fmt, _("staged"), _("unstaged"), _("path")); From patchwork Tue Aug 27 12:58:01 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Arver via GitGitGadget X-Patchwork-Id: 11116885 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id F1000174A for ; Tue, 27 Aug 2019 12:58:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id CF7212077B for ; Tue, 27 Aug 2019 12:58:06 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="QIifbr6l" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726735AbfH0M6G (ORCPT ); Tue, 27 Aug 2019 08:58:06 -0400 Received: from mail-wr1-f66.google.com ([209.85.221.66]:39667 "EHLO mail-wr1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729935AbfH0M6E (ORCPT ); Tue, 27 Aug 2019 08:58:04 -0400 Received: by mail-wr1-f66.google.com with SMTP id t16so18707266wra.6 for ; Tue, 27 Aug 2019 05:58:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:message-id:in-reply-to:references:from:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=MDS3VyEtjqlxL22NPuOn2c1gYCHbJCWQ5MSerLrc6II=; b=QIifbr6lTbBUoCzOogOa13Zf+NnRpeedVpOXIJ95zjY7V24E8sSmhFcEviSjnPtv5t vRSzbvfAhAguZacM/9n2+dBV+h2PtH5W2ZD9xjGrfM8heJg+UWzK22iIPY2BrWVEcXG9 jyese+kvD5OrhxPNGu3HPW1jchu94431BOi0NTnYV8Kp/gxPEsmBbxK+A+I7wgzMTu4D cwL3dkks26nFRIFKTqcEHHjKzFRSOT7FqBw98Ol0WV3tBFcyJj3zaccCTI+hSUmJdowO NTKQl0z/RdpqxrVFJqBI5H79V4lPZ+wbqNuYOkmUEaF31kRHSe43JtJpm3GpWhsJSYjD buLw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:message-id:in-reply-to:references:from :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=MDS3VyEtjqlxL22NPuOn2c1gYCHbJCWQ5MSerLrc6II=; b=DNiXWQBB/UpYADIgFMFD8+jVcyZuPHzB8XnnfUPVJSBpCjjc9dpK0L6YbAWjeLIpci T+R8w8zP5hiazAHI03qCjj7PlkkNYvOtR5fjK+EjxNLqlBlhLR9TPH/yhkNle+d4LoxR iP6i5UUutb0Lq5qHjABfoI16ij8kWxK/Si/HauI9aE6IFj7uvoGpAryTg+rp9TovK40c QXX6TtC20mNuSeaKnfsWOScoTxhFXgUxD01CMhArFgyn0k9Vb4KVpWXXQQ3PbWjI8p/Z HnNH9zN67u0unHPT57PtnjzArJn+btLb0bmYt65JWdD+Dr5dFPWRmPhY3+cmgaOVFCLb 0yvw== X-Gm-Message-State: APjAAAV1coDGXKmyy/Oz/rHzJ8bpHVs4oVAhfJ33dNRuocf2q52V+blh q8lo+eRXtKJukX4r4yS37/fLuRRARMU= X-Google-Smtp-Source: APXvYqxEPcu0I/52WsG5s+qH2nCmyRQxNeGIAPudfjo0mE1uhEVZWhDy7BNYugA7LQGLXfEXodXO3w== X-Received: by 2002:adf:e78c:: with SMTP id n12mr28063373wrm.83.1566910682300; Tue, 27 Aug 2019 05:58:02 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id l62sm4559416wml.13.2019.08.27.05.58.01 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 27 Aug 2019 05:58:01 -0700 (PDT) Date: Tue, 27 Aug 2019 05:58:01 -0700 (PDT) X-Google-Original-Date: Tue, 27 Aug 2019 12:57:52 GMT Message-Id: In-Reply-To: References: From: "Johannes Schindelin via GitGitGadget" Subject: [PATCH v4 11/11] built-in add -i: implement the `help` command Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: Jeff Hostetler , Jeff King , Junio C Hamano , Johannes Schindelin Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Johannes Schindelin This imitates the code to show the help text from the Perl script `git-add--interactive.perl` in the built-in version. To make sure that it renders exactly like the Perl version of `git add -i`, we also add a test case for that to `t3701-add-interactive.sh`. Signed-off-by: Slavica Djukic Signed-off-by: Johannes Schindelin --- add-interactive.c | 26 ++++++++++++++++++++++++-- t/t3701-add-interactive.sh | 25 +++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/add-interactive.c b/add-interactive.c index a343195a67..765455a3fc 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -429,6 +429,26 @@ static int run_status(struct add_i_state *s, const struct pathspec *ps, return 0; } +static int run_help(struct add_i_state *s, const struct pathspec *unused_ps, + struct file_list *unused_files, + struct list_options *unused_opts) +{ + color_fprintf_ln(stdout, s->help_color, "status - %s", + _("show paths with changes")); + color_fprintf_ln(stdout, s->help_color, "update - %s", + _("add working tree state to the staged set of changes")); + color_fprintf_ln(stdout, s->help_color, "revert - %s", + _("revert staged set of changes back to the HEAD version")); + color_fprintf_ln(stdout, s->help_color, "patch - %s", + _("pick hunks and update selectively")); + color_fprintf_ln(stdout, s->help_color, "diff - %s", + _("view diff between HEAD and index")); + color_fprintf_ln(stdout, s->help_color, "add untracked - %s", + _("add contents of untracked files to the staged set of changes")); + + return 0; +} + struct print_command_item_data { const char *color, *reset; }; @@ -474,9 +494,11 @@ int run_add_i(struct repository *r, const struct pathspec *ps) N_("What now"), command_prompt_help }; struct command_item - status = { { "status" }, run_status }; + status = { { "status" }, run_status }, + help = { { "help" }, run_help }; struct command_item *commands[] = { - &status + &status, + &help }; struct print_file_item_data print_file_item_data = { diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 69991a3168..cf67756b85 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -647,4 +647,29 @@ test_expect_success 'checkout -p works with pathological context lines' ' test_write_lines a b a b a a b a b a >expect && test_cmp expect a ' + +test_expect_success 'show help from add--helper' ' + git reset --hard && + cat >expect <<-EOF && + + *** Commands *** + 1: status 2: update 3: revert 4: add untracked + 5: patch 6: diff 7: quit 8: help + What now> status - show paths with changes + update - add working tree state to the staged set of changes + revert - revert staged set of changes back to the HEAD version + patch - pick hunks and update selectively + diff - view diff between HEAD and index + add untracked - add contents of untracked files to the staged set of changes + *** Commands *** + 1: status 2: update 3: revert 4: add untracked + 5: patch 6: diff 7: quit 8: help + What now>$SP + Bye. + EOF + test_write_lines h | GIT_PAGER_IN_USE=true TERM=vt100 git add -i >actual.colored && + test_decode_color actual && + test_i18ncmp expect actual +' + test_done