* [PATCH 6/7] Revert "reset: Make reset remove the sequencer state"
From: Jonathan Nieder @ 2011-12-10 13:03 UTC (permalink / raw)
To: Johannes Sixt
Cc: Junio C Hamano, Ramkumar Ramachandra, git, Christian Couder,
Martin von Zweigbergk, Jay Soffian
In-Reply-To: <20111210124644.GA22035@elie.hsd1.il.comcast.net>
This reverts commit 95eb88d8ee588d89b4f06d2753ed4d16ab13b39f, which
was a UI experiment that did not reflect how "git reset" actually gets
used. The reversion also fixes a test, indicated in the patch.
Encouraged-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
branch.c | 2 -
t/t3510-cherry-pick-sequence.sh | 2 +-
t/t7106-reset-sequence.sh | 52 ---------------------------------------
3 files changed, 1 insertions(+), 55 deletions(-)
delete mode 100755 t/t7106-reset-sequence.sh
diff --git a/branch.c b/branch.c
index 025a97be..a6b6722e 100644
--- a/branch.c
+++ b/branch.c
@@ -3,7 +3,6 @@
#include "refs.h"
#include "remote.h"
#include "commit.h"
-#include "sequencer.h"
struct tracking {
struct refspec spec;
@@ -247,5 +246,4 @@ void remove_branch_state(void)
unlink(git_path("MERGE_MSG"));
unlink(git_path("MERGE_MODE"));
unlink(git_path("SQUASH_MSG"));
- remove_sequencer_state(0);
}
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 851b147f..e80050e1 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -353,7 +353,7 @@ test_expect_success '--continue asks for help after resolving patch to nil' '
test_i18ngrep "The previous cherry-pick is now empty" msg
'
-test_expect_failure 'follow advice and skip nil patch' '
+test_expect_success 'follow advice and skip nil patch' '
pristine_detach conflicting &&
test_must_fail git cherry-pick initial..picked &&
diff --git a/t/t7106-reset-sequence.sh b/t/t7106-reset-sequence.sh
deleted file mode 100755
index 83f7ea59..00000000
--- a/t/t7106-reset-sequence.sh
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/bin/sh
-
-test_description='Test interaction of reset --hard with sequencer
-
- + anotherpick: rewrites foo to d
- + picked: rewrites foo to c
- + unrelatedpick: rewrites unrelated to reallyunrelated
- + base: rewrites foo to b
- + initial: writes foo as a, unrelated as unrelated
-'
-
-. ./test-lib.sh
-
-pristine_detach () {
- git cherry-pick --quit &&
- git checkout -f "$1^0" &&
- git read-tree -u --reset HEAD &&
- git clean -d -f -f -q -x
-}
-
-test_expect_success setup '
- echo unrelated >unrelated &&
- git add unrelated &&
- test_commit initial foo a &&
- test_commit base foo b &&
- test_commit unrelatedpick unrelated reallyunrelated &&
- test_commit picked foo c &&
- test_commit anotherpick foo d &&
- git config advice.detachedhead false
-
-'
-
-test_expect_success 'reset --hard cleans up sequencer state, providing one-level undo' '
- pristine_detach initial &&
- test_must_fail git cherry-pick base..anotherpick &&
- test_path_is_dir .git/sequencer &&
- git reset --hard &&
- test_path_is_missing .git/sequencer &&
- test_path_is_dir .git/sequencer-old &&
- git reset --hard &&
- test_path_is_missing .git/sequencer-old
-'
-
-test_expect_success 'cherry-pick --abort does not leave sequencer-old dir' '
- pristine_detach initial &&
- test_must_fail git cherry-pick base..anotherpick &&
- git cherry-pick --abort &&
- test_path_is_missing .git/sequencer &&
- test_path_is_missing .git/sequencer-old
-'
-
-test_done
--
1.7.8.rc3
^ permalink raw reply related
* [PATCH 5/7] revert: do not remove state until sequence is finished
From: Jonathan Nieder @ 2011-12-10 13:02 UTC (permalink / raw)
To: Johannes Sixt
Cc: Junio C Hamano, Ramkumar Ramachandra, git, Christian Couder,
Martin von Zweigbergk, Jay Soffian
In-Reply-To: <20111210124644.GA22035@elie.hsd1.il.comcast.net>
As v1.7.8-rc0~141^2~4 (2011-08-04) explains, git cherry-pick removes
the sequencer state just before applying the final patch. In the
single-pick case, that was a good thing, since --abort and --continue
work fine without access to such state and removing it provides a
signal that git should not complain about the need to clobber it ("a
cherry-pick or revert is already in progress") in sequences like the
following:
git cherry-pick foo
git read-tree -m -u HEAD; # forget that; let's try a different one
git cherry-pick bar
After the recent patch "allow single-pick in the middle of cherry-pick
sequence" we don't need that hack any more. In the new regime, a
traditional "git cherry-pick <commit>" command never looks at
.git/sequencer, so we do not need to cripple "git cherry-pick
<commit>..<commit>" for it any more.
So now you can run "git cherry-pick --abort" near the end of a
multi-pick sequence and it will abort the entire sequence, instead of
misbehaving and aborting just the final commit.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
builtin/revert.c | 12 +-----------
t/t3510-cherry-pick-sequence.sh | 6 +++---
2 files changed, 4 insertions(+), 14 deletions(-)
diff --git a/builtin/revert.c b/builtin/revert.c
index dcb69904..28deb85b 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -1018,18 +1018,8 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
for (cur = todo_list; cur; cur = cur->next) {
save_todo(cur, opts);
res = do_pick_commit(cur->item, opts);
- if (res) {
- if (!cur->next)
- /*
- * An error was encountered while
- * picking the last commit; the
- * sequencer state is useless now --
- * the user simply needs to resolve
- * the conflict and commit
- */
- remove_sequencer_state(0);
+ if (res)
return res;
- }
}
/*
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 98a27a23..851b147f 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -203,10 +203,10 @@ test_expect_success '--abort refuses to clobber unrelated change, harder case' '
test_cmp_rev initial HEAD
'
-test_expect_success 'cherry-pick cleans up sequencer state when one commit is left' '
+test_expect_success 'cherry-pick still writes sequencer state when one commit is left' '
pristine_detach initial &&
test_must_fail git cherry-pick base..picked &&
- test_path_is_missing .git/sequencer &&
+ test_path_is_dir .git/sequencer &&
echo "resolved" >foo &&
git add foo &&
git commit &&
@@ -227,7 +227,7 @@ test_expect_success 'cherry-pick cleans up sequencer state when one commit is le
test_cmp expect actual
'
-test_expect_failure '--abort after last commit in sequence' '
+test_expect_success '--abort after last commit in sequence' '
pristine_detach initial &&
test_must_fail git cherry-pick base..picked &&
git cherry-pick --abort &&
--
1.7.8.rc3
^ permalink raw reply related
* [PATCH 4/7] revert: allow single-pick in the middle of cherry-pick sequence
From: Jonathan Nieder @ 2011-12-10 12:59 UTC (permalink / raw)
To: Johannes Sixt
Cc: Junio C Hamano, Ramkumar Ramachandra, git, Christian Couder,
Martin von Zweigbergk, Jay Soffian
In-Reply-To: <20111210124644.GA22035@elie.hsd1.il.comcast.net>
When I messed up a difficult conflict in the middle of a cherry-pick
sequence, it can be useful to be able to 'git checkout HEAD . && git
cherry-pick that-one-commit' to restart the conflict resolution.
Suggested-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Maybe this should come after patch 6/7, so the use case could use
"git reset --hard".
builtin/revert.c | 26 ++++++++++++++++++++++++++
t/t3510-cherry-pick-sequence.sh | 12 ++++++++++++
2 files changed, 38 insertions(+), 0 deletions(-)
diff --git a/builtin/revert.c b/builtin/revert.c
index 71570357..dcb69904 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -1072,6 +1072,12 @@ static int sequencer_continue(struct replay_opts *opts)
return pick_commits(todo_list, opts);
}
+static int single_pick(struct commit *cmit, struct replay_opts *opts)
+{
+ setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
+ return do_pick_commit(cmit, opts);
+}
+
static int pick_revisions(struct replay_opts *opts)
{
struct commit_list *todo_list = NULL;
@@ -1097,6 +1103,26 @@ static int pick_revisions(struct replay_opts *opts)
return sequencer_continue(opts);
/*
+ * If we were called as "git cherry-pick <commit>", just
+ * cherry-pick/revert it, set CHERRY_PICK_HEAD /
+ * REVERT_HEAD, and don't touch the sequencer state.
+ * This means it is possible to cherry-pick in the middle
+ * of a cherry-pick sequence.
+ */
+ if (opts->revs->cmdline.nr == 1 &&
+ opts->revs->cmdline.rev->whence == REV_CMD_REV &&
+ opts->revs->no_walk &&
+ !opts->revs->cmdline.rev->flags) {
+ struct commit *cmit;
+ if (prepare_revision_walk(opts->revs))
+ die(_("revision walk setup failed"));
+ cmit = get_revision(opts->revs);
+ if (!cmit || get_revision(opts->revs))
+ die("BUG: expected exactly one commit from walk");
+ return single_pick(cmit, opts);
+ }
+
+ /*
* Start a new cherry-pick/ revert sequence; but
* first, make sure that an existing one isn't in
* progress
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 56c95ec1..98a27a23 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -50,6 +50,18 @@ test_expect_success 'cherry-pick persists data on failure' '
test_path_is_file .git/sequencer/opts
'
+test_expect_success 'cherry-pick mid-cherry-pick-sequence' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick base..anotherpick &&
+ test_cmp_rev picked CHERRY_PICK_HEAD &&
+ # "oops, I forgot that these patches rely on the change from base"
+ git checkout HEAD foo &&
+ git cherry-pick base &&
+ git cherry-pick picked &&
+ git cherry-pick --continue &&
+ git diff --exit-code anotherpick
+'
+
test_expect_success 'cherry-pick persists opts correctly' '
pristine_detach initial &&
test_must_fail git cherry-pick -s -m 1 --strategy=recursive -X patience -X ours base..anotherpick &&
--
1.7.8.rc3
^ permalink raw reply related
* [PATCH 3/7] revert: pass around rev-list args in already-parsed form
From: Jonathan Nieder @ 2011-12-10 12:58 UTC (permalink / raw)
To: Johannes Sixt
Cc: Junio C Hamano, Ramkumar Ramachandra, git, Christian Couder,
Martin von Zweigbergk, Jay Soffian
In-Reply-To: <20111210124644.GA22035@elie.hsd1.il.comcast.net>
Date: Sat, 13 Aug 2011 12:06:23 -0500
Since 7e2bfd3f (revert: allow cherry-picking more than one commit,
2010-07-02), the pick/revert machinery has kept track of the set of
commits to be cherry-picked or reverted using commit_argc and
commit_argv variables, storing the corresponding command-line
parameters.
Future callers as other commands are built in (am, rebase, sequencer)
may find it easier to pass rev-list options to this machinery in
already-parsed form. Teach cmd_cherry_pick and cmd_revert to parse
the rev-list arguments in advance and pass the commit set to
pick_revisions() as a rev_info structure.
Original patch by Jonathan, tweaks and test from Ram.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Improved-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
builtin/revert.c | 53 +++++++++++++++++++++-----------------
t/t3510-cherry-pick-sequence.sh | 5 +++
2 files changed, 34 insertions(+), 24 deletions(-)
diff --git a/builtin/revert.c b/builtin/revert.c
index a43b4d85..71570357 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -60,13 +60,14 @@ struct replay_opts {
int allow_rerere_auto;
int mainline;
- int commit_argc;
- const char **commit_argv;
/* Merge strategy */
const char *strategy;
const char **xopts;
size_t xopts_nr, xopts_alloc;
+
+ /* Only used by REPLAY_NONE */
+ struct rev_info *revs;
};
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
@@ -169,9 +170,9 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
die(_("program error"));
}
- opts->commit_argc = parse_options(argc, argv, NULL, options, usage_str,
- PARSE_OPT_KEEP_ARGV0 |
- PARSE_OPT_KEEP_UNKNOWN);
+ argc = parse_options(argc, argv, NULL, options, usage_str,
+ PARSE_OPT_KEEP_ARGV0 |
+ PARSE_OPT_KEEP_UNKNOWN);
/* Check for incompatible subcommands */
verify_opt_mutually_compatible(me,
@@ -213,9 +214,6 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
NULL);
}
- else if (opts->commit_argc < 2)
- usage_with_options(usage_str, options);
-
if (opts->allow_ff)
verify_opt_compatible(me, "--ff",
"--signoff", opts->signoff,
@@ -223,7 +221,20 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
"-x", opts->record_origin,
"--edit", opts->edit,
NULL);
- opts->commit_argv = argv;
+
+ if (opts->subcommand != REPLAY_NONE) {
+ opts->revs = NULL;
+ } else {
+ opts->revs = xmalloc(sizeof(*opts->revs));
+ init_revisions(opts->revs, NULL);
+ opts->revs->no_walk = 1;
+ if (argc < 2)
+ usage_with_options(usage_str, options);
+ argc = setup_revisions(argc, argv, opts->revs, NULL);
+ }
+
+ if (argc > 1)
+ usage_with_options(usage_str, options);
}
struct commit_message {
@@ -631,23 +642,15 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
return res;
}
-static void prepare_revs(struct rev_info *revs, struct replay_opts *opts)
+static void prepare_revs(struct replay_opts *opts)
{
- int argc;
-
- init_revisions(revs, NULL);
- revs->no_walk = 1;
if (opts->action != REVERT)
- revs->reverse = 1;
+ opts->revs->reverse ^= 1;
- argc = setup_revisions(opts->commit_argc, opts->commit_argv, revs, NULL);
- if (argc > 1)
- usage(*revert_or_cherry_pick_usage(opts));
-
- if (prepare_revision_walk(revs))
+ if (prepare_revision_walk(opts->revs))
die(_("revision walk setup failed"));
- if (!revs->commits)
+ if (!opts->revs->commits)
die(_("empty commit set passed"));
}
@@ -844,14 +847,13 @@ static void read_populate_opts(struct replay_opts **opts_ptr)
static void walk_revs_populate_todo(struct commit_list **todo_list,
struct replay_opts *opts)
{
- struct rev_info revs;
struct commit *commit;
struct commit_list **next;
- prepare_revs(&revs, opts);
+ prepare_revs(opts);
next = todo_list;
- while ((commit = get_revision(&revs)))
+ while ((commit = get_revision(opts->revs)))
next = commit_list_append(commit, next);
}
@@ -1075,6 +1077,9 @@ static int pick_revisions(struct replay_opts *opts)
struct commit_list *todo_list = NULL;
unsigned char sha1[20];
+ if (opts->subcommand == REPLAY_NONE)
+ assert(opts->revs);
+
read_and_refresh_cache(opts);
/*
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 4d1883b7..56c95ec1 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -461,4 +461,9 @@ test_expect_success 'malformed instruction sheet 2' '
test_must_fail git cherry-pick --continue
'
+test_expect_success 'empty commit set' '
+ pristine_detach initial &&
+ test_expect_code 128 git cherry-pick base..base
+'
+
test_done
--
1.7.8.rc3
^ permalink raw reply related
* [PATCH 3/3] Rename resolve_ref() to resolve_ref_unsafe()
From: Nguyễn Thái Ngọc Duy @ 2011-12-10 12:53 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jonathan Nieder, Tony Wang,
Nguyễn Thái Ngọc Duy
In-Reply-To: <1323521631-24320-1-git-send-email-pclouds@gmail.com>
resolve_ref() may return a pointer to a shared buffer and can be
overwritten by the next resolve_ref() calls. Callers need to
pay attention, not to keep the pointer when the next call happens.
Rename with "_unsafe" suffix to warn developers (or reviewers) before
introducing new call sites.
This patch is generated using this command
git grep -l 'resolve_ref(' -- '*.[ch]'|xargs sed -i 's/resolve_ref(/resolve_ref_unsafe(/g'
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
branch.c | 2 +-
builtin/branch.c | 2 +-
builtin/commit.c | 2 +-
builtin/fsck.c | 2 +-
builtin/log.c | 4 ++--
builtin/receive-pack.c | 2 +-
builtin/remote.c | 2 +-
builtin/show-branch.c | 2 +-
builtin/symbolic-ref.c | 2 +-
cache.h | 2 +-
refs.c | 20 ++++++++++----------
remote.c | 6 +++---
test-resolve-ref.c | 4 ++--
transport.c | 2 +-
14 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/branch.c b/branch.c
index d91a099..772a4c2 100644
--- a/branch.c
+++ b/branch.c
@@ -182,7 +182,7 @@ int validate_new_branchname(const char *name, struct strbuf *ref,
const char *head;
unsigned char sha1[20];
- head = resolve_ref("HEAD", sha1, 0, NULL);
+ head = resolve_ref_unsafe("HEAD", sha1, 0, NULL);
if (!is_bare_repository() && head && !strcmp(head, ref->buf))
die("Cannot force update the current branch.");
}
diff --git a/builtin/branch.c b/builtin/branch.c
index 72c4c31..641bee1 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -251,7 +251,7 @@ static char *resolve_symref(const char *src, const char *prefix)
int flag;
const char *dst, *cp;
- dst = resolve_ref(src, sha1, 0, &flag);
+ dst = resolve_ref_unsafe(src, sha1, 0, &flag);
if (!(dst && (flag & REF_ISSYMREF)))
return NULL;
if (prefix && (cp = skip_prefix(dst, prefix)))
diff --git a/builtin/commit.c b/builtin/commit.c
index e36e9ad..4d39d25 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1304,7 +1304,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
rev.diffopt.break_opt = 0;
diff_setup_done(&rev.diffopt);
- head = resolve_ref("HEAD", junk_sha1, 0, NULL);
+ head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL);
printf("[%s%s ",
!prefixcmp(head, "refs/heads/") ?
head + 11 :
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 30d0dc8..8c479a7 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -563,7 +563,7 @@ static int fsck_head_link(void)
if (verbose)
fprintf(stderr, "Checking HEAD link\n");
- head_points_at = resolve_ref("HEAD", head_sha1, 0, &flag);
+ head_points_at = resolve_ref_unsafe("HEAD", head_sha1, 0, &flag);
if (!head_points_at)
return error("Invalid HEAD");
if (!strcmp(head_points_at, "HEAD"))
diff --git a/builtin/log.c b/builtin/log.c
index 4395f3e..89d0cc0 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1040,7 +1040,7 @@ static char *find_branch_name(struct rev_info *rev)
if (positive < 0)
return NULL;
strbuf_addf(&buf, "refs/heads/%s", rev->cmdline.rev[positive].name);
- branch = resolve_ref(buf.buf, branch_sha1, 1, NULL);
+ branch = resolve_ref_unsafe(buf.buf, branch_sha1, 1, NULL);
if (!branch ||
prefixcmp(branch, "refs/heads/") ||
hashcmp(rev->cmdline.rev[positive].item->sha1, branch_sha1))
@@ -1268,7 +1268,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.pending.objects[0].item->flags |= UNINTERESTING;
add_head_to_pending(&rev);
- ref = resolve_ref("HEAD", sha1, 1, NULL);
+ ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
if (ref && !prefixcmp(ref, "refs/heads/"))
branch_name = xstrdup(ref + strlen("refs/heads/"));
else
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 5cd6fc7..a1a4b09 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -571,7 +571,7 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
int flag;
strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
- dst_name = resolve_ref(buf.buf, sha1, 0, &flag);
+ dst_name = resolve_ref_unsafe(buf.buf, sha1, 0, &flag);
strbuf_release(&buf);
if (!(flag & REF_ISSYMREF))
diff --git a/builtin/remote.c b/builtin/remote.c
index 407abfb..583eec9 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -573,7 +573,7 @@ static int read_remote_branches(const char *refname,
strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
if (!prefixcmp(refname, buf.buf)) {
item = string_list_append(rename->remote_branches, xstrdup(refname));
- symref = resolve_ref(refname, orig_sha1, 1, &flag);
+ symref = resolve_ref_unsafe(refname, orig_sha1, 1, &flag);
if (flag & REF_ISSYMREF)
item->util = xstrdup(symref);
else
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index a1f148e..a59e088 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -789,7 +789,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
}
}
- head_p = resolve_ref("HEAD", head_sha1, 1, NULL);
+ head_p = resolve_ref_unsafe("HEAD", head_sha1, 1, NULL);
if (head_p) {
head_len = strlen(head_p);
memcpy(head, head_p, head_len + 1);
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index dea849c..2ef5962 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -12,7 +12,7 @@ static void check_symref(const char *HEAD, int quiet)
{
unsigned char sha1[20];
int flag;
- const char *refs_heads_master = resolve_ref(HEAD, sha1, 0, &flag);
+ const char *refs_heads_master = resolve_ref_unsafe(HEAD, sha1, 0, &flag);
if (!refs_heads_master)
die("No such ref: %s", HEAD);
diff --git a/cache.h b/cache.h
index a63d890..3ce8bda 100644
--- a/cache.h
+++ b/cache.h
@@ -865,7 +865,7 @@ extern int read_ref(const char *filename, unsigned char *sha1);
*
* errno is sometimes set on errors, but not always.
*/
-#define resolve_ref(ref, sha1, reading, flag) resolve_ref_real(ref, sha1, reading, flag, __FUNCTION__, __LINE__)
+#define resolve_ref_unsafe(ref, sha1, reading, flag) resolve_ref_real(ref, sha1, reading, flag, __FUNCTION__, __LINE__)
extern const char *resolve_ref_real(const char *ref, unsigned char *sha1, int reading, int *flag, const char *file, int line);
extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
diff --git a/refs.c b/refs.c
index cf8dfcc..010bb72 100644
--- a/refs.c
+++ b/refs.c
@@ -361,7 +361,7 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
if (!(flags & REF_ISSYMREF))
return 0;
- resolves_to = resolve_ref(refname, junk, 0, NULL);
+ resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL);
if (!resolves_to || strcmp(resolves_to, d->refname))
return 0;
@@ -616,7 +616,7 @@ const char *resolve_ref_real(const char *ref, unsigned char *sha1,
char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
{
- const char *ret = resolve_ref(ref, sha1, reading, flag);
+ const char *ret = resolve_ref_unsafe(ref, sha1, reading, flag);
return ret ? xstrdup(ret) : NULL;
}
@@ -629,7 +629,7 @@ struct ref_filter {
int read_ref_full(const char *ref, unsigned char *sha1, int reading, int *flags)
{
- if (resolve_ref(ref, sha1, reading, flags))
+ if (resolve_ref_unsafe(ref, sha1, reading, flags))
return 0;
return -1;
}
@@ -1126,7 +1126,7 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
this_result = refs_found ? sha1_from_ref : sha1;
mksnpath(fullref, sizeof(fullref), *p, len, str);
- r = resolve_ref(fullref, this_result, 1, &flag);
+ r = resolve_ref_unsafe(fullref, this_result, 1, &flag);
if (r) {
if (!refs_found++)
*ref = xstrdup(r);
@@ -1156,7 +1156,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
const char *ref, *it;
mksnpath(path, sizeof(path), *p, len, str);
- ref = resolve_ref(path, hash, 1, NULL);
+ ref = resolve_ref_unsafe(path, hash, 1, NULL);
if (!ref)
continue;
if (!stat(git_path("logs/%s", path), &st) &&
@@ -1192,7 +1192,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
lock = xcalloc(1, sizeof(struct ref_lock));
lock->lock_fd = -1;
- ref = resolve_ref(ref, lock->old_sha1, mustexist, &type);
+ ref = resolve_ref_unsafe(ref, lock->old_sha1, mustexist, &type);
if (!ref && errno == EISDIR) {
/* we are trying to lock foo but we used to
* have foo/bar which now does not exist;
@@ -1205,7 +1205,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
error("there are still refs under '%s'", orig_ref);
goto error_return;
}
- ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, &type);
+ ref = resolve_ref_unsafe(orig_ref, lock->old_sha1, mustexist, &type);
}
if (type_p)
*type_p = type;
@@ -1368,7 +1368,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
if (log && S_ISLNK(loginfo.st_mode))
return error("reflog for %s is a symlink", oldref);
- symref = resolve_ref(oldref, orig_sha1, 1, &flag);
+ symref = resolve_ref_unsafe(oldref, orig_sha1, 1, &flag);
if (flag & REF_ISSYMREF)
return error("refname %s is a symbolic ref, renaming it is not supported",
oldref);
@@ -1657,7 +1657,7 @@ int write_ref_sha1(struct ref_lock *lock,
unsigned char head_sha1[20];
int head_flag;
const char *head_ref;
- head_ref = resolve_ref("HEAD", head_sha1, 1, &head_flag);
+ head_ref = resolve_ref_unsafe("HEAD", head_sha1, 1, &head_flag);
if (head_ref && (head_flag & REF_ISSYMREF) &&
!strcmp(head_ref, lock->ref_name))
log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
@@ -1994,7 +1994,7 @@ int update_ref(const char *action, const char *refname,
int ref_exists(const char *refname)
{
unsigned char sha1[20];
- return !!resolve_ref(refname, sha1, 1, NULL);
+ return !!resolve_ref_unsafe(refname, sha1, 1, NULL);
}
struct ref *find_ref_by_name(const struct ref *list, const char *name)
diff --git a/remote.c b/remote.c
index 6655bb0..73a3809 100644
--- a/remote.c
+++ b/remote.c
@@ -482,7 +482,7 @@ static void read_config(void)
return;
default_remote_name = xstrdup("origin");
current_branch = NULL;
- head_ref = resolve_ref("HEAD", sha1, 0, &flag);
+ head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
if (head_ref && (flag & REF_ISSYMREF) &&
!prefixcmp(head_ref, "refs/heads/")) {
current_branch =
@@ -1007,7 +1007,7 @@ static char *guess_ref(const char *name, struct ref *peer)
struct strbuf buf = STRBUF_INIT;
unsigned char sha1[20];
- const char *r = resolve_ref(peer->name, sha1, 1, NULL);
+ const char *r = resolve_ref_unsafe(peer->name, sha1, 1, NULL);
if (!r)
return NULL;
@@ -1058,7 +1058,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
unsigned char sha1[20];
int flag;
- dst_value = resolve_ref(matched_src->name, sha1, 1, &flag);
+ dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag);
if (!dst_value ||
((flag & REF_ISSYMREF) &&
prefixcmp(dst_value, "refs/heads/")))
diff --git a/test-resolve-ref.c b/test-resolve-ref.c
index 934d764..1847cb4 100644
--- a/test-resolve-ref.c
+++ b/test-resolve-ref.c
@@ -5,8 +5,8 @@ int main(int argc, char **argv)
unsigned char sha1[20];
const char *ref1, *ref2;
setup_git_directory();
- ref1 = resolve_ref("HEAD", sha1, 0, NULL);
- ref2 = resolve_ref("HEAD", sha1, 0, NULL);
+ ref1 = resolve_ref_unsafe("HEAD", sha1, 0, NULL);
+ ref2 = resolve_ref_unsafe("HEAD", sha1, 0, NULL);
printf("ref1 = %s\nref2 = %s\n", ref1, ref2);
return 0;
}
diff --git a/transport.c b/transport.c
index 51814b5..e9797c0 100644
--- a/transport.c
+++ b/transport.c
@@ -163,7 +163,7 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
/* Follow symbolic refs (mainly for HEAD). */
localname = ref->peer_ref->name;
remotename = ref->name;
- tmp = resolve_ref(localname, sha, 1, &flag);
+ tmp = resolve_ref_unsafe(localname, sha, 1, &flag);
if (tmp && flag & REF_ISSYMREF &&
!prefixcmp(tmp, "refs/heads/"))
localname = tmp;
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH 2/3] Guard memory overwriting in resolve_ref's static buffer
From: Nguyễn Thái Ngọc Duy @ 2011-12-10 12:53 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jonathan Nieder, Tony Wang,
Nguyễn Thái Ngọc Duy
In-Reply-To: <1323521631-24320-1-git-send-email-pclouds@gmail.com>
There is a potential problem with resolve_ref() and some other
functions in git. The return value returned by resolve_ref() may
be changed when the function is called again. Callers must make sure the
next call won't happen as long as the value is still being used.
It's usually hard to track down this kind of problem. Michael Haggerty
has an idea [1] that, instead of passing the same static buffer to
caller every time the function is called, we free the old buffer and
allocate the new one. This way access to the old (now invalid) buffer
may be caught.
This patch applies the same principle for resolve_ref() with a
few modifications:
- This behavior is enabled when GIT_DEBUG_MEMCHECK is set. The ability
is always available. We may be able to ask users to rerun with this
flag on in suspicious cases.
- Rely on mmap/mprotect to catch illegal access. We need valgrind or
some other memory tracking tool to reliably catch this in Michael's
approach.
- Because mprotect is used instead of munmap, we definitely leak
memory. Hopefully callers will not put resolve_ref() in a
loop that runs 1 million times.
- Save caller location in the allocated buffer so we know who made this
call in the core dump.
Also introduce a new target, "make memcheck", that runs tests with this
flag on.
[1] http://comments.gmane.org/gmane.comp.version-control.git/182209
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
.gitignore | 1 +
Makefile | 4 ++++
cache.h | 3 ++-
git-compat-util.h | 9 +++++++++
refs.c | 13 +++++++++++--
t/t0071-memcheck.sh | 12 ++++++++++++
test-resolve-ref.c | 12 ++++++++++++
wrapper.c | 22 ++++++++++++++++++++++
8 files changed, 73 insertions(+), 3 deletions(-)
create mode 100755 t/t0071-memcheck.sh
create mode 100644 test-resolve-ref.c
diff --git a/.gitignore b/.gitignore
index 8572c8c..470e452 100644
--- a/.gitignore
+++ b/.gitignore
@@ -179,6 +179,7 @@
/test-obj-pool
/test-parse-options
/test-path-utils
+/test-resolve-ref
/test-run-command
/test-sha1
/test-sigchain
diff --git a/Makefile b/Makefile
index ed82320..d71cf04 100644
--- a/Makefile
+++ b/Makefile
@@ -444,6 +444,7 @@ TEST_PROGRAMS_NEED_X += test-obj-pool
TEST_PROGRAMS_NEED_X += test-parse-options
TEST_PROGRAMS_NEED_X += test-path-utils
TEST_PROGRAMS_NEED_X += test-run-command
+TEST_PROGRAMS_NEED_X += test-resolve-ref
TEST_PROGRAMS_NEED_X += test-sha1
TEST_PROGRAMS_NEED_X += test-sigchain
TEST_PROGRAMS_NEED_X += test-string-pool
@@ -2241,6 +2242,9 @@ export NO_SVN_TESTS
test: all
$(MAKE) -C t/ all
+memcheck: all
+ GIT_DEBUG_MEMCHECK=1 $(MAKE) -C t/ all
+
test-ctype$X: ctype.o
test-date$X: date.o ctype.o
diff --git a/cache.h b/cache.h
index 4887a3e..a63d890 100644
--- a/cache.h
+++ b/cache.h
@@ -865,7 +865,8 @@ extern int read_ref(const char *filename, unsigned char *sha1);
*
* errno is sometimes set on errors, but not always.
*/
-extern const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag);
+#define resolve_ref(ref, sha1, reading, flag) resolve_ref_real(ref, sha1, reading, flag, __FUNCTION__, __LINE__)
+extern const char *resolve_ref_real(const char *ref, unsigned char *sha1, int reading, int *flag, const char *file, int line);
extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
diff --git a/git-compat-util.h b/git-compat-util.h
index 77062ed..9249fc2 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -433,6 +433,15 @@ extern char *xstrndup(const char *str, size_t len);
extern void *xrealloc(void *ptr, size_t size);
extern void *xcalloc(size_t nmemb, size_t size);
extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+
+/*
+ * These functions are used to allocate new memory. When the memory
+ * area is no longer used, ban all access to it so any illegal access
+ * can be caught. xfree_mmap() does not really free memory.
+ */
+extern void *xmalloc_mmap(size_t, const char *file, int line);
+extern void xfree_mmap(void *);
+
extern ssize_t xread(int fd, void *buf, size_t len);
extern ssize_t xwrite(int fd, const void *buf, size_t len);
extern int xdup(int fd);
diff --git a/refs.c b/refs.c
index 8ffb32f..cf8dfcc 100644
--- a/refs.c
+++ b/refs.c
@@ -497,12 +497,21 @@ static int get_packed_ref(const char *ref, unsigned char *sha1)
return -1;
}
-const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
+const char *resolve_ref_real(const char *ref, unsigned char *sha1,
+ int reading, int *flag, const char *file, int line)
{
int depth = MAXDEPTH;
ssize_t len;
char buffer[256];
- static char ref_buffer[256];
+ static char real_ref_buffer[256];
+ static char *ref_buffer;
+
+ if (!ref_buffer && !getenv("GIT_DEBUG_MEMCHECK"))
+ ref_buffer = real_ref_buffer;
+ if (ref_buffer != real_ref_buffer) {
+ xfree_mmap(ref_buffer);
+ ref_buffer = xmalloc_mmap(256, file, line);
+ }
if (flag)
*flag = 0;
diff --git a/t/t0071-memcheck.sh b/t/t0071-memcheck.sh
new file mode 100755
index 0000000..b594732
--- /dev/null
+++ b/t/t0071-memcheck.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+test_description='test that GIT_DEBUG_MEMCHECK works correctly'
+. ./test-lib.sh
+
+test_expect_success 'test-resolve-ref must crash' '
+ GIT_DEBUG_MEMCHECK=1 test-resolve-ref
+ exit_code=$? &&
+ test $exit_code -eq 139
+'
+
+test_done
diff --git a/test-resolve-ref.c b/test-resolve-ref.c
new file mode 100644
index 0000000..934d764
--- /dev/null
+++ b/test-resolve-ref.c
@@ -0,0 +1,12 @@
+#include "cache.h"
+
+int main(int argc, char **argv)
+{
+ unsigned char sha1[20];
+ const char *ref1, *ref2;
+ setup_git_directory();
+ ref1 = resolve_ref("HEAD", sha1, 0, NULL);
+ ref2 = resolve_ref("HEAD", sha1, 0, NULL);
+ printf("ref1 = %s\nref2 = %s\n", ref1, ref2);
+ return 0;
+}
diff --git a/wrapper.c b/wrapper.c
index 85f09df..407443a 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -60,6 +60,28 @@ void *xmallocz(size_t size)
return ret;
}
+void *xmalloc_mmap(size_t size, const char *file, int line)
+{
+ int *ret;
+ size += sizeof(int*) * 3;
+ ret = mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (ret == (int*)-1)
+ die_errno("unable to mmap %lu bytes anonymously",
+ (unsigned long)size);
+
+ ret[0] = (int)file;
+ ret[1] = line;
+ ret[2] = size;
+ return ret + 3;
+}
+
+void xfree_mmap(void *p)
+{
+ if (p && mprotect(((int*)p) - 3, ((int*)p)[-1], PROT_NONE) == -1)
+ die_errno("unable to remove memory access");
+}
+
/*
* xmemdupz() allocates (len + 1) bytes of memory, duplicates "len" bytes of
* "data" to the allocated memory, zero terminates the allocated memory,
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH 1/3] Convert resolve_ref+xstrdup to new resolve_refdup function
From: Nguyễn Thái Ngọc Duy @ 2011-12-10 12:53 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jonathan Nieder, Tony Wang,
Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/branch.c | 11 ++++-------
builtin/checkout.c | 15 +++++++--------
builtin/fmt-merge-msg.c | 6 +++---
builtin/for-each-ref.c | 7 ++-----
builtin/merge.c | 12 +++++-------
builtin/notes.c | 6 +++---
builtin/receive-pack.c | 8 +++-----
builtin/revert.c | 2 +-
builtin/show-branch.c | 4 +---
cache.h | 1 +
reflog-walk.c | 13 ++++++-------
refs.c | 6 ++++++
wt-status.c | 4 +---
13 files changed, 43 insertions(+), 52 deletions(-)
diff --git a/builtin/branch.c b/builtin/branch.c
index e1e486e..72c4c31 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -103,7 +103,7 @@ static int branch_merged(int kind, const char *name,
* safely to HEAD (or the other branch).
*/
struct commit *reference_rev = NULL;
- const char *reference_name = NULL;
+ char *reference_name = NULL;
int merged;
if (kind == REF_LOCAL_BRANCH) {
@@ -115,10 +115,8 @@ static int branch_merged(int kind, const char *name,
branch->merge[0] &&
branch->merge[0]->dst &&
(reference_name =
- resolve_ref(branch->merge[0]->dst, sha1, 1, NULL)) != NULL) {
- reference_name = xstrdup(reference_name);
+ resolve_refdup(branch->merge[0]->dst, sha1, 1, NULL)) != NULL)
reference_rev = lookup_commit_reference(sha1);
- }
}
if (!reference_rev)
reference_rev = head_rev;
@@ -143,7 +141,7 @@ static int branch_merged(int kind, const char *name,
" '%s', even though it is merged to HEAD."),
name, reference_name);
}
- free((char *)reference_name);
+ free(reference_name);
return merged;
}
@@ -731,10 +729,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
track = git_branch_track;
- head = resolve_ref("HEAD", head_sha1, 0, NULL);
+ head = resolve_refdup("HEAD", head_sha1, 0, NULL);
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
- head = xstrdup(head);
if (!strcmp(head, "HEAD")) {
detached = 1;
} else {
diff --git a/builtin/checkout.c b/builtin/checkout.c
index b7c6302..a66d3eb 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -696,17 +696,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
{
int ret = 0;
struct branch_info old;
+ char *path;
unsigned char rev[20];
int flag;
memset(&old, 0, sizeof(old));
- old.path = resolve_ref("HEAD", rev, 0, &flag);
- if (old.path)
- old.path = xstrdup(old.path);
+ old.path = path = resolve_refdup("HEAD", rev, 0, &flag);
old.commit = lookup_commit_reference_gently(rev, 1);
- if (!(flag & REF_ISSYMREF)) {
- free((char *)old.path);
+ if (!(flag & REF_ISSYMREF))
old.path = NULL;
- }
if (old.path && !prefixcmp(old.path, "refs/heads/"))
old.name = old.path + strlen("refs/heads/");
@@ -720,8 +717,10 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
}
ret = merge_working_tree(opts, &old, new);
- if (ret)
+ if (ret) {
+ free(path);
return ret;
+ }
if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
orphaned_commit_warning(old.commit);
@@ -729,7 +728,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
update_refs_for_switch(opts, &old, new);
ret = post_checkout_hook(old.commit, new->commit, 1);
- free((char *)old.path);
+ free(path);
return ret || opts->writeout_error;
}
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index bdfa0ea..a27efcd 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -372,14 +372,14 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
int i = 0, pos = 0;
unsigned char head_sha1[20];
const char *current_branch;
+ char *ref;
/* get current branch */
- current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
+ current_branch = ref = resolve_refdup("HEAD", head_sha1, 1, NULL);
if (!current_branch)
die("No current branch");
if (!prefixcmp(current_branch, "refs/heads/"))
current_branch += 11;
- current_branch = xstrdup(current_branch);
/* get a line */
while (pos < in->len) {
@@ -421,7 +421,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
}
strbuf_complete_line(out);
- free((char *)current_branch);
+ free(ref);
return 0;
}
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index d90e5d2..b01d76a 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -628,11 +628,8 @@ static void populate_value(struct refinfo *ref)
if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
unsigned char unused1[20];
- const char *symref;
- symref = resolve_ref(ref->refname, unused1, 1, NULL);
- if (symref)
- ref->symref = xstrdup(symref);
- else
+ ref->symref = resolve_refdup(ref->refname, unused1, 1, NULL);
+ if (!ref->symref)
ref->symref = "";
}
diff --git a/builtin/merge.c b/builtin/merge.c
index a1c8534..6437db2 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -1096,6 +1096,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
struct commit_list *common = NULL;
const char *best_strategy = NULL, *wt_strategy = NULL;
struct commit_list **remotes = &remoteheads;
+ char *branch_ref;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_merge_usage, builtin_merge_options);
@@ -1104,12 +1105,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
*/
- branch = resolve_ref("HEAD", head_sha1, 0, &flag);
- if (branch) {
- if (!prefixcmp(branch, "refs/heads/"))
- branch += 11;
- branch = xstrdup(branch);
- }
+ branch = branch_ref = resolve_refdup("HEAD", head_sha1, 0, &flag);
+ if (branch && !prefixcmp(branch, "refs/heads/"))
+ branch += 11;
if (!branch || is_null_sha1(head_sha1))
head_commit = NULL;
else
@@ -1520,6 +1518,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
ret = suggest_conflicts(option_renormalize);
done:
- free((char *)branch);
+ free(branch_ref);
return ret;
}
diff --git a/builtin/notes.c b/builtin/notes.c
index 10b8bc7..816c998 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -804,6 +804,7 @@ static int merge_commit(struct notes_merge_options *o)
struct notes_tree *t;
struct commit *partial;
struct pretty_print_context pretty_ctx;
+ char *ref;
int ret;
/*
@@ -826,10 +827,9 @@ static int merge_commit(struct notes_merge_options *o)
t = xcalloc(1, sizeof(struct notes_tree));
init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
- o->local_ref = resolve_ref("NOTES_MERGE_REF", sha1, 0, NULL);
+ o->local_ref = ref = resolve_refdup("NOTES_MERGE_REF", sha1, 0, NULL);
if (!o->local_ref)
die("Failed to resolve NOTES_MERGE_REF");
- o->local_ref = xstrdup(o->local_ref);
if (notes_merge_commit(o, t, partial, sha1))
die("Failed to finalize notes merge");
@@ -846,7 +846,7 @@ static int merge_commit(struct notes_merge_options *o)
free_notes(t);
strbuf_release(&msg);
ret = merge_abort(o);
- free((char *)o->local_ref);
+ free(ref);
return ret;
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index b6d957c..5cd6fc7 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -36,7 +36,7 @@ static int use_sideband;
static int prefer_ofs_delta = 1;
static int auto_update_server_info;
static int auto_gc = 1;
-static const char *head_name;
+static char *head_name;
static int sent_capabilities;
static enum deny_action parse_deny_action(const char *var, const char *value)
@@ -695,10 +695,8 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
check_aliased_updates(commands);
- free((char *)head_name);
- head_name = resolve_ref("HEAD", sha1, 0, NULL);
- if (head_name)
- head_name = xstrdup(head_name);
+ free(head_name);
+ head_name = resolve_refdup("HEAD", sha1, 0, NULL);
for (cmd = commands; cmd; cmd = cmd->next)
if (!cmd->skip_update)
diff --git a/builtin/revert.c b/builtin/revert.c
index 1ea525c..0c52a83 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -901,7 +901,7 @@ static int rollback_single_pick(void)
if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
!file_exists(git_path("REVERT_HEAD")))
return error(_("no cherry-pick or revert in progress"));
- if (!resolve_ref("HEAD", head_sha1, 0, NULL))
+ if (read_ref_full("HEAD", head_sha1, 0, NULL))
return error(_("cannot resolve HEAD"));
if (is_null_sha1(head_sha1))
return error(_("cannot abort from a branch yet to be born"));
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 4b480d7..a1f148e 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -726,10 +726,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
if (ac == 0) {
static const char *fake_av[2];
- const char *refname;
- refname = resolve_ref("HEAD", sha1, 1, NULL);
- fake_av[0] = xstrdup(refname);
+ fake_av[0] = resolve_refdup("HEAD", sha1, 1, NULL);
fake_av[1] = NULL;
av = fake_av;
ac = 1;
diff --git a/cache.h b/cache.h
index 8c98d05..4887a3e 100644
--- a/cache.h
+++ b/cache.h
@@ -866,6 +866,7 @@ extern int read_ref(const char *filename, unsigned char *sha1);
* errno is sometimes set on errors, but not always.
*/
extern const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag);
+extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
diff --git a/reflog-walk.c b/reflog-walk.c
index da71a85..5572b42 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -50,11 +50,10 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
for_each_reflog_ent(ref, read_one_reflog, reflogs);
if (reflogs->nr == 0) {
unsigned char sha1[20];
- const char *name = resolve_ref(ref, sha1, 1, NULL);
+ char *name = resolve_refdup(ref, sha1, 1, NULL);
if (name) {
- name = xstrdup(name);
for_each_reflog_ent(name, read_one_reflog, reflogs);
- free((char *)name);
+ free(name);
}
}
if (reflogs->nr == 0) {
@@ -171,11 +170,11 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
else {
if (*branch == '\0') {
unsigned char sha1[20];
- const char *head = resolve_ref("HEAD", sha1, 0, NULL);
- if (!head)
- die ("No current branch");
free(branch);
- branch = xstrdup(head);
+ branch = resolve_refdup("HEAD", sha1, 0, NULL);
+ if (!branch)
+ die ("No current branch");
+
}
reflogs = read_complete_reflog(branch);
if (!reflogs || reflogs->nr == 0) {
diff --git a/refs.c b/refs.c
index f5cb297..8ffb32f 100644
--- a/refs.c
+++ b/refs.c
@@ -605,6 +605,12 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
return ref;
}
+char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
+{
+ const char *ret = resolve_ref(ref, sha1, reading, flag);
+ return ret ? xstrdup(ret) : NULL;
+}
+
/* The argument to filter_refs */
struct ref_filter {
const char *pattern;
diff --git a/wt-status.c b/wt-status.c
index 70fdb76..9ffc535 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -111,7 +111,6 @@ void status_printf_more(struct wt_status *s, const char *color,
void wt_status_prepare(struct wt_status *s)
{
unsigned char sha1[20];
- const char *head;
memset(s, 0, sizeof(*s));
memcpy(s->color_palette, default_wt_status_colors,
@@ -119,8 +118,7 @@ void wt_status_prepare(struct wt_status *s)
s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
s->use_color = -1;
s->relative_paths = 1;
- head = resolve_ref("HEAD", sha1, 0, NULL);
- s->branch = head ? xstrdup(head) : NULL;
+ s->branch = resolve_refdup("HEAD", sha1, 0, NULL);
s->reference = "HEAD";
s->fp = stdout;
s->index_file = get_index_file();
--
1.7.8.36.g69ee2
^ permalink raw reply related
* [PATCH 2/7] revert: allow cherry-pick --continue to commit before resuming
From: Jonathan Nieder @ 2011-12-10 12:49 UTC (permalink / raw)
To: Johannes Sixt
Cc: Junio C Hamano, Ramkumar Ramachandra, git, Christian Couder,
Martin von Zweigbergk, Jay Soffian
In-Reply-To: <20111210124644.GA22035@elie.hsd1.il.comcast.net>
When "git cherry-pick ..bar" encounters conflicts, permit the operator
to use cherry-pick --continue after resolving them as a shortcut for
"git commit && git cherry-pick --continue" to record the resolution
and carry on with the rest of the sequence.
This improves the analogy with "git rebase" (in olden days --continue
was the way to preserve authorship when a rebase encountered
conflicts) and fits well with a general UI goal of making "git cmd
--continue" save humans the trouble of deciding what to do next.
Example: after encountering a conflict from running "git cherry-pick
foo bar baz":
CONFLICT (content): Merge conflict in main.c
error: could not apply f78a8d98c... bar!
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'
We edit main.c to resolve the conflict, mark it acceptable with "git
add main.c", and can run "cherry-pick --continue" to resume the
sequence.
$ git cherry-pick --continue
[editor opens to confirm commit message]
[master 78c8a8c98] bar!
1 files changed, 1 insertions(+), 1 deletions(-)
[master 87ca8798c] baz!
1 files changed, 1 insertions(+), 1 deletions(-)
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
builtin/revert.c | 23 ++++++-
t/t3510-cherry-pick-sequence.sh | 139 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 156 insertions(+), 6 deletions(-)
diff --git a/builtin/revert.c b/builtin/revert.c
index 9f6c85c1..a43b4d85 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -1038,18 +1038,35 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
return 0;
}
+static int continue_single_pick(void)
+{
+ const char *argv[] = { "commit", NULL };
+
+ if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
+ !file_exists(git_path("REVERT_HEAD")))
+ return error(_("no cherry-pick or revert in progress"));
+ return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
static int sequencer_continue(struct replay_opts *opts)
{
struct commit_list *todo_list = NULL;
if (!file_exists(git_path(SEQ_TODO_FILE)))
- return error(_("No %s in progress"), action_name(opts));
+ return continue_single_pick();
read_populate_opts(&opts);
read_populate_todo(&todo_list, opts);
/* Verify that the conflict has been resolved */
- if (!index_differs_from("HEAD", 0))
- todo_list = todo_list->next;
+ if (file_exists(git_path("CHERRY_PICK_HEAD")) ||
+ file_exists(git_path("REVERT_HEAD"))) {
+ int ret = continue_single_pick();
+ if (ret)
+ return ret;
+ }
+ if (index_differs_from("HEAD", 0))
+ return error_dirty_index(opts);
+ todo_list = todo_list->next;
return pick_commits(todo_list, opts);
}
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 2c4c1c85..4d1883b7 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -2,6 +2,7 @@
test_description='Test cherry-pick continuation features
+ + conflicting: rewrites unrelated to conflicting
+ yetanotherpick: rewrites foo to e
+ anotherpick: rewrites foo to d
+ picked: rewrites foo to c
@@ -27,6 +28,7 @@ test_cmp_rev () {
}
test_expect_success setup '
+ git config advice.detachedhead false
echo unrelated >unrelated &&
git add unrelated &&
test_commit initial foo a &&
@@ -35,8 +37,8 @@ test_expect_success setup '
test_commit picked foo c &&
test_commit anotherpick foo d &&
test_commit yetanotherpick foo e &&
- git config advice.detachedhead false
-
+ pristine_detach initial &&
+ test_commit conflicting unrelated
'
test_expect_success 'cherry-pick persists data on failure' '
@@ -243,7 +245,66 @@ test_expect_success '--continue complains when there are unresolved conflicts' '
test_must_fail git cherry-pick --continue
'
-test_expect_success '--continue continues after conflicts are resolved' '
+test_expect_success '--continue of single cherry-pick' '
+ pristine_detach initial &&
+ echo c >expect &&
+ test_must_fail git cherry-pick picked &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+
+ test_cmp expect foo &&
+ test_cmp_rev initial HEAD^ &&
+ git diff --exit-code HEAD &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
+'
+
+test_expect_success '--continue of single revert' '
+ pristine_detach initial &&
+ echo resolved >expect &&
+ echo "Revert \"picked\"" >expect.msg &&
+ test_must_fail git revert picked &&
+ echo resolved >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+
+ git diff --exit-code HEAD &&
+ test_cmp expect foo &&
+ test_cmp_rev initial HEAD^ &&
+ git diff-tree -s --pretty=tformat:%s HEAD >msg &&
+ test_cmp expect.msg msg &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+ test_must_fail git rev-parse --verify REVERT_HEAD
+'
+
+test_expect_success '--continue after resolving conflicts' '
+ pristine_detach initial &&
+ echo d >expect &&
+ cat >expect.log <<-\EOF &&
+ OBJID
+ :100644 100644 OBJID OBJID M foo
+ OBJID
+ :100644 100644 OBJID OBJID M foo
+ OBJID
+ :100644 100644 OBJID OBJID M unrelated
+ OBJID
+ :000000 100644 OBJID OBJID A foo
+ :000000 100644 OBJID OBJID A unrelated
+ EOF
+ test_must_fail git cherry-pick base..anotherpick &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+ {
+ git rev-list HEAD |
+ git diff-tree --root --stdin |
+ sed "s/$_x40/OBJID/g"
+ } >actual.log &&
+ test_cmp expect foo &&
+ test_cmp expect.log actual.log
+'
+
+test_expect_success '--continue after resolving conflicts and committing' '
pristine_detach initial &&
test_must_fail git cherry-pick base..anotherpick &&
echo "c" >foo &&
@@ -270,6 +331,29 @@ test_expect_success '--continue continues after conflicts are resolved' '
test_cmp expect actual
'
+test_expect_success '--continue asks for help after resolving patch to nil' '
+ pristine_detach conflicting &&
+ test_must_fail git cherry-pick initial..picked &&
+
+ test_cmp_rev unrelatedpick CHERRY_PICK_HEAD &&
+ git checkout HEAD -- unrelated &&
+ test_must_fail git cherry-pick --continue 2>msg &&
+ test_i18ngrep "The previous cherry-pick is now empty" msg
+'
+
+test_expect_failure 'follow advice and skip nil patch' '
+ pristine_detach conflicting &&
+ test_must_fail git cherry-pick initial..picked &&
+
+ git checkout HEAD -- unrelated &&
+ test_must_fail git cherry-pick --continue &&
+ git reset &&
+ git cherry-pick --continue &&
+
+ git rev-list initial..HEAD >commits &&
+ test_line_count = 3 commits
+'
+
test_expect_success '--continue respects opts' '
pristine_detach initial &&
test_must_fail git cherry-pick -x base..anotherpick &&
@@ -288,6 +372,29 @@ test_expect_success '--continue respects opts' '
grep "cherry picked from" anotherpick_msg
'
+test_expect_success '--continue of single-pick respects -x' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick -x picked &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+ test_path_is_missing .git/sequencer &&
+ git cat-file commit HEAD >msg &&
+ grep "cherry picked from" msg
+'
+
+test_expect_success '--continue respects -x in first commit in multi-pick' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick -x picked anotherpick &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+ test_path_is_missing .git/sequencer &&
+ git cat-file commit HEAD^ >msg &&
+ picked=$(git rev-parse --verify picked) &&
+ grep "cherry picked from.*$picked" msg
+'
+
test_expect_success '--signoff is not automatically propagated to resolved conflict' '
pristine_detach initial &&
test_must_fail git cherry-pick --signoff base..anotherpick &&
@@ -306,6 +413,32 @@ test_expect_success '--signoff is not automatically propagated to resolved confl
grep "Signed-off-by:" anotherpick_msg
'
+test_expect_success '--signoff dropped for implicit commit of resolution, multi-pick case' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick -s picked anotherpick &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+
+ git diff --exit-code HEAD &&
+ test_cmp_rev initial HEAD^^ &&
+ git cat-file commit HEAD^ >msg &&
+ ! grep Signed-off-by: msg
+'
+
+test_expect_success 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick -s picked &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+
+ git diff --exit-code HEAD &&
+ test_cmp_rev initial HEAD^ &&
+ git cat-file commit HEAD >msg &&
+ ! grep Signed-off-by: msg
+'
+
test_expect_success 'malformed instruction sheet 1' '
pristine_detach initial &&
test_must_fail git cherry-pick base..anotherpick &&
--
1.7.8.rc3
^ permalink raw reply related
* [PATCH 1/7] revert: give --continue handling its own function
From: Jonathan Nieder @ 2011-12-10 12:47 UTC (permalink / raw)
To: Johannes Sixt
Cc: Junio C Hamano, Ramkumar Ramachandra, git, Christian Couder,
Martin von Zweigbergk, Jay Soffian
In-Reply-To: <20111210124644.GA22035@elie.hsd1.il.comcast.net>
This makes pick_revisions() a little shorter and easier to read
straight through.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
builtin/revert.c | 28 +++++++++++++++++-----------
1 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/builtin/revert.c b/builtin/revert.c
index 1ea525c1..9f6c85c1 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -1038,6 +1038,21 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
return 0;
}
+static int sequencer_continue(struct replay_opts *opts)
+{
+ struct commit_list *todo_list = NULL;
+
+ if (!file_exists(git_path(SEQ_TODO_FILE)))
+ return error(_("No %s in progress"), action_name(opts));
+ read_populate_opts(&opts);
+ read_populate_todo(&todo_list, opts);
+
+ /* Verify that the conflict has been resolved */
+ if (!index_differs_from("HEAD", 0))
+ todo_list = todo_list->next;
+ return pick_commits(todo_list, opts);
+}
+
static int pick_revisions(struct replay_opts *opts)
{
struct commit_list *todo_list = NULL;
@@ -1056,17 +1071,8 @@ static int pick_revisions(struct replay_opts *opts)
}
if (opts->subcommand == REPLAY_ROLLBACK)
return sequencer_rollback(opts);
- if (opts->subcommand == REPLAY_CONTINUE) {
- if (!file_exists(git_path(SEQ_TODO_FILE)))
- return error(_("No %s in progress"), action_name(opts));
- read_populate_opts(&opts);
- read_populate_todo(&todo_list, opts);
-
- /* Verify that the conflict has been resolved */
- if (!index_differs_from("HEAD", 0))
- todo_list = todo_list->next;
- return pick_commits(todo_list, opts);
- }
+ if (opts->subcommand == REPLAY_CONTINUE)
+ return sequencer_continue(opts);
/*
* Start a new cherry-pick/ revert sequence; but
--
1.7.8.rc3
^ permalink raw reply related
* [RFC/PATCH 0/7] some sequencer loose ends (Re: Fix revert --abort on Windows)
From: Jonathan Nieder @ 2011-12-10 12:46 UTC (permalink / raw)
To: Johannes Sixt
Cc: Junio C Hamano, Ramkumar Ramachandra, git, Christian Couder,
Martin von Zweigbergk, Jay Soffian
In-Reply-To: <4ECCC935.7010407@viscovery.net>
(-cc: Phil)
Johannes Sixt wrote:
> IMO, it doesn't make sense that git-reset aborts a cherry-pick sequence:
> When I messed up a difficult conflict in the middle of a cherry-pick
> sequence, it might be useful to be able to 'git reset --hard && git
> cherry-pick that-one-commit' to restart the conflict resolution.
>
> (But does a single-commit cherry-pick during a multi-commit cherry-pick
> work to begin with?)
It should, I think.
Here are patches to address some UI warts (such as that one) in
current cherry-pick code.
Patch 1 cleans up to prepare for patch 2, which in turn makes "git
cherry-pick --continue" act like "git rebase --continue" by commiting
a conflict resolution if it has not already been committed. This
brings us closer to a less confusing world in which all commands that
can exit and ask the user to help to get closer to their goal provide
a --continue option as a standard interface to resume (I guess "git
merge --continue" is all that's left to do afterwards).
Patch 3 is from Ram's rr/revert-cherry-pick series. It doesn't have
much to do with this series, but I'd rather work on a codebase with
this particular patch applied, so I applied it before working on
patch 4.
Patch 4 uses Junio's cmdline_info API to distinguish single-picks
from multi-picks. This is the title feature. For a while I thought
something like this was the only sane thing to do, but laziness won
out.
Patches 5-7 remove hacks that patch 4 makes superfluous.
Patch 7 has the downside that if anyone had a .git/sequencer-old
directory lying around, then git will not care after applying the
patch and it will sit just taking up its few bytes and distracting
people that run "ls .git". I'm not sure whether that's worth fixing,
and if so how.
Anyway, I hope you enjoy the series. Thoughts and bug reports
appreciated as usual.
Jonathan Nieder (7):
revert: give --continue handling its own function
revert: allow cherry-pick --continue to commit before resuming
revert: pass around rev-list args in already-parsed form
revert: allow single-pick in the middle of cherry-pick sequence
revert: do not remove state until sequence is finished
Revert "reset: Make reset remove the sequencer state"
revert: stop creating or removing sequencer-old directory
branch.c | 2 -
builtin/revert.c | 138 ++++++++++++++++++++++-----------
sequencer.c | 10 +--
sequencer.h | 12 +---
t/t3510-cherry-pick-sequence.sh | 162 +++++++++++++++++++++++++++++++++++++--
t/t7106-reset-sequence.sh | 52 -------------
6 files changed, 251 insertions(+), 125 deletions(-)
delete mode 100755 t/t7106-reset-sequence.sh
^ permalink raw reply
* best way to fastforward all tracking branches after a fetch
From: Gelonida N @ 2011-12-10 12:26 UTC (permalink / raw)
To: git
Hi,
What is the best way to fastforward all fastforwardable tracking
branches after a git fetch?
^ permalink raw reply
* Re: process committed files in post-receive hook
From: Ivan Heffner @ 2011-12-10 12:06 UTC (permalink / raw)
To: Michael Schubert, Hao; +Cc: git
In-Reply-To: <4EE340D2.200@elegosoft.com>
On Sat, Dec 10, 2011 at 3:21 AM, Michael Schubert <mschub@elegosoft.com> wrote:
> On 12/10/2011 11:29 AM, Hao wrote:
>> I am writing a post-receive hook in Python that examines the content of some
>> files (the HEAD rev). Because the repo is a bare one on the server. My current
>> approach is to check out a working copy on the server and run 'git pull' in post-
>> receive to get the most up-to-date version, and then process files in the
>> working copy.
>
You can actually use a combination of git ls-files and git cat-file -p
in order to list and look at te content of files on the remote without
checking out an entire working tree.
^ permalink raw reply
* Re: [PATCHv3 11/13] strbuf: add strbuf_add*_urlencode
From: Jakub Narebski @ 2011-12-10 11:57 UTC (permalink / raw)
To: Jeff King; +Cc: Junio C Hamano, git
In-Reply-To: <20111210103420.GK16529@sigill.intra.peff.net>
Jeff King <peff@peff.net> writes:
> +void strbuf_add_urlencode(struct strbuf *sb, const char *s, size_t len,
> + int reserved)
> +{
> + strbuf_grow(sb, len);
What is this `reserved` flag for, and when should one use it?
It would be nice to have a short comment...
BTW. was it meant to be aligned like this?
> +void strbuf_add_urlencode(struct strbuf *sb, const char *s, size_t len,
> + int reserved)
--
Jakub Narębski
^ permalink raw reply
* Re: [PATCHv3 08/13] credential: make relevance of http path configurable
From: Jakub Narebski @ 2011-12-10 11:50 UTC (permalink / raw)
To: Jeff King; +Cc: Junio C Hamano, git
In-Reply-To: <20111210103133.GH16529@sigill.intra.peff.net>
Jeff King <peff@peff.net> writes:
[...]
> This is nothing you couldn't do with a clever credential
> helper at the start of your stack, like:
>
> [credential "http://"]
> helper = "!f() { grep -v ^path= ; }; f"
> helper = your_real_helper
>
> But doing this:
>
> [credential]
> useHttpPath = false
Shouldn't this be 'usePath' or 'usePathComponent' or 'useRepositoryPath',
etc.? Because if^W when remote helper for Subversion is complete, you
could have svn://svnserve.example.com/path/to/repo as an URL... which
would be not HTTP(S).
--
Jakub Narębski
^ permalink raw reply
* Re: [PATCHv3 03/13] introduce credentials API
From: Jakub Narebski @ 2011-12-10 11:43 UTC (permalink / raw)
To: Jeff King; +Cc: Junio C Hamano, git
In-Reply-To: <20111210103111.GC16529@sigill.intra.peff.net>
Jeff King <peff@peff.net> writes:
> There are a few places in git that need to get a username
> and password credential from the user; the most notable one
> is HTTP authentication for smart-http pushing.
A question: does it work also for access via SSH, either without
public key set up (i.e. 'keyboard-interactive'), or with encrypted
private key without ssh-agent set up?
It would probably require URL i.e. ssh://git.example.com/srv/scm/repo.git
or git+ssh://git.example.com/srv/scm/repo.git and not scp-like
address i.e. user@git.example.com:/srv/scm/repo.git, isn't it?
--
Jakub Narębski
^ permalink raw reply
* Re: [RFC/PATCH] show tracking branches with their associated branch names
From: Santhosh Kumar Mani @ 2011-12-10 11:23 UTC (permalink / raw)
To: Vincent van Ravesteijn; +Cc: git
In-Reply-To: <4EE32C1B.8070306@lyx.org>
On Sat, 2011-12-10 at 10:53 +0100, Vincent van Ravesteijn wrote:
> Op 10-12-2011 8:40, Santhosh Kumar Mani schreef:
> > The "git branch" command, by default, displays the local branches. There
> > is no visual distinction made between the tracking branches and normal
> > local branches. This patch enables the "git branch" to display
> > tracking info for tracking branches:
> >
> > Before this patch:
> > $ git branch
> > * master
> > local
> >
> > After this patch:
> > $ git branch
> > * master [origin/master]
> > local
> >
> >
> Did you try "git branch -vv" ?
>
Yes. I did. It is too verbose as it meant to be.
This patch distinguishes between the tracking and non-tracking local
branches.
I use a lot of tracking branches and they track different branches. I
tend find this feature useful to know which branch tracks what.
Of course, I could get this information in different ways. But it makes
sense to have this information displayed by default.
> Vincent
Regards,
Santhosh.
^ permalink raw reply
* Re: process committed files in post-receive hook
From: Michael Schubert @ 2011-12-10 11:21 UTC (permalink / raw)
To: Hao; +Cc: git
In-Reply-To: <loom.20111210T111457-837@post.gmane.org>
On 12/10/2011 11:29 AM, Hao wrote:
> I am writing a post-receive hook in Python that examines the content of some
> files (the HEAD rev). Because the repo is a bare one on the server. My current
> approach is to check out a working copy on the server and run 'git pull' in post-
> receive to get the most up-to-date version, and then process files in the
> working copy.
You could do something like this as a post-receive hook:
#!/bin/sh
test_dir=$(mktemp -d /tmp/test.XXXXXXXXXX)
GIT_WORK_TREE=$test_dir git checkout -f
/usr/local/bin/check.py $test_dir
rm -rf $test_dir
^ permalink raw reply
* [PATCH 1/1] contrib: add credential helper for OS X Keychain
From: Jeff King @ 2011-12-10 10:53 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jay Soffian, git
In-Reply-To: <20111210102827.GA16460@sigill.intra.peff.net>
On Sat, Dec 10, 2011 at 05:28:27AM -0500, Jeff King wrote:
> Here's the latest re-roll of the credential helpers series. I think this
> one is probably ready to go to 'next'.
>
> It's rebased on the latest tip of 'master' (applying it to an older
> commit will get you a minor textual conflict in strbuf.c). It
> incorporates the erase-safety we discussed, fixes a few commit message
> typos, and tweaks the test scripts to make testing the external OS X
> helper a little easier.
And here is that helper.
-- >8 --
Subject: [PATCH] contrib: add credential helper for OS X Keychain
With this installed in your $PATH, you can store
git-over-http passwords in your keychain by doing:
git config credential.helper osxkeychain
The code is based in large part on the work of Jay Soffian,
who wrote the helper originally for the initial, unpublished
version of the credential helper protocol.
This version will pass t0303 if you do:
GIT_TEST_CREDENTIAL_HELPER=osxkeychain \
GIT_TEST_CREDENTIAL_HELPER_SETUP="export HOME=$HOME" \
./t0303-credential-external.sh
The "HOME" setup is unfortunately necessary. The test
scripts set HOME to the trash directory, but this causes the
keychain API to complain.
Signed-off-by: Jeff King <peff@peff.net>
---
I tried clever workarounds for the HOME thing, like running "security
create-keychain" in the trash directory. But if I try to create the
"login" keychain, it says it's already there! So it's like we're in a
weird limbo where we both have and do not have access to the keychains.
I'm not too concerned with the hackishness of my solution, though; this
isn't even a part of the regular test scripts, but just a thing you can
do manually to test the helper from contrib.
Many thanks to Jay for the initial version; even though this version is
quite chopped-up, having somebody else provide a nice example of how to
use the SecKeychain functions made it much easier. I tried to trim the
code down to the bare essentials to glue our input into the SecKeychain
API, and to show good practices for other helpers to follow.
I dropped the python version, as it is redundant and I didn't feel like
porting it to the new interface. It could be added on top if somebody
feels like converting it as an exercise. :)
contrib/credential/osxkeychain/.gitignore | 1 +
contrib/credential/osxkeychain/Makefile | 14 ++
.../osxkeychain/git-credential-osxkeychain.c | 173 ++++++++++++++++++++
3 files changed, 188 insertions(+), 0 deletions(-)
create mode 100644 contrib/credential/osxkeychain/.gitignore
create mode 100644 contrib/credential/osxkeychain/Makefile
create mode 100644 contrib/credential/osxkeychain/git-credential-osxkeychain.c
diff --git a/contrib/credential/osxkeychain/.gitignore b/contrib/credential/osxkeychain/.gitignore
new file mode 100644
index 0000000..6c5b702
--- /dev/null
+++ b/contrib/credential/osxkeychain/.gitignore
@@ -0,0 +1 @@
+git-credential-osxkeychain
diff --git a/contrib/credential/osxkeychain/Makefile b/contrib/credential/osxkeychain/Makefile
new file mode 100644
index 0000000..75c07f8
--- /dev/null
+++ b/contrib/credential/osxkeychain/Makefile
@@ -0,0 +1,14 @@
+all:: git-credential-osxkeychain
+
+CC = gcc
+RM = rm -f
+CFLAGS = -g -Wall
+
+git-credential-osxkeychain: git-credential-osxkeychain.o
+ $(CC) -o $@ $< -Wl,-framework -Wl,Security
+
+git-credential-osxkeychain.o: git-credential-osxkeychain.c
+ $(CC) -c $(CFLAGS) $<
+
+clean:
+ $(RM) git-credential-osxkeychain git-credential-osxkeychain.o
diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
new file mode 100644
index 0000000..6beed12
--- /dev/null
+++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
@@ -0,0 +1,173 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <Security/Security.h>
+
+static SecProtocolType protocol;
+static char *host;
+static char *path;
+static char *username;
+static char *password;
+static UInt16 port;
+
+static void die(const char *err, ...)
+{
+ char msg[4096];
+ va_list params;
+ va_start(params, err);
+ vsnprintf(msg, sizeof(msg), err, params);
+ fprintf(stderr, "%s\n", msg);
+ va_end(params);
+ exit(1);
+}
+
+static void *xstrdup(const char *s1)
+{
+ void *ret = strdup(s1);
+ if (!ret)
+ die("Out of memory");
+ return ret;
+}
+
+#define KEYCHAIN_ITEM(x) (x ? strlen(x) : 0), x
+#define KEYCHAIN_ARGS \
+ NULL, /* default keychain */ \
+ KEYCHAIN_ITEM(host), \
+ 0, NULL, /* account domain */ \
+ KEYCHAIN_ITEM(username), \
+ KEYCHAIN_ITEM(path), \
+ port, \
+ protocol, \
+ kSecAuthenticationTypeDefault
+
+static void write_item(const char *what, const char *buf, int len)
+{
+ printf("%s=", what);
+ fwrite(buf, 1, len, stdout);
+ putchar('\n');
+}
+
+static void find_username_in_item(SecKeychainItemRef item)
+{
+ SecKeychainAttributeList list;
+ SecKeychainAttribute attr;
+
+ list.count = 1;
+ list.attr = &attr;
+ attr.tag = kSecAccountItemAttr;
+
+ if (SecKeychainItemCopyContent(item, NULL, &list, NULL, NULL))
+ return;
+
+ write_item("username", attr.data, attr.length);
+ SecKeychainItemFreeContent(&list, NULL);
+}
+
+static void find_internet_password(void)
+{
+ void *buf;
+ UInt32 len;
+ SecKeychainItemRef item;
+
+ if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, &len, &buf, &item))
+ return;
+
+ write_item("password", buf, len);
+ if (!username)
+ find_username_in_item(item);
+
+ SecKeychainItemFreeContent(NULL, buf);
+}
+
+static void delete_internet_password(void)
+{
+ SecKeychainItemRef item;
+
+ /*
+ * Require at least a protocol and host for removal, which is what git
+ * will give us; if you want to do something more fancy, use the
+ * Keychain manager.
+ */
+ if (!protocol || !host)
+ return;
+
+ if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, 0, NULL, &item))
+ return;
+
+ SecKeychainItemDelete(item);
+}
+
+static void add_internet_password(void)
+{
+ /* Only store complete credentials */
+ if (!protocol || !host || !username || !password)
+ return;
+
+ if (SecKeychainAddInternetPassword(
+ KEYCHAIN_ARGS,
+ KEYCHAIN_ITEM(password),
+ NULL))
+ return;
+}
+
+static void read_credential(void)
+{
+ char buf[1024];
+
+ while (fgets(buf, sizeof(buf), stdin)) {
+ char *v;
+
+ if (!strcmp(buf, "\n"))
+ break;
+ buf[strlen(buf)-1] = '\0';
+
+ v = strchr(buf, '=');
+ if (!v)
+ die("bad input: %s", buf);
+ *v++ = '\0';
+
+ if (!strcmp(buf, "protocol")) {
+ if (!strcmp(v, "https"))
+ protocol = kSecProtocolTypeHTTPS;
+ else if (!strcmp(v, "http"))
+ protocol = kSecProtocolTypeHTTP;
+ else /* we don't yet handle other protocols */
+ exit(0);
+ }
+ else if (!strcmp(buf, "host")) {
+ char *colon = strchr(v, ':');
+ if (colon) {
+ *colon++ = '\0';
+ port = atoi(colon);
+ }
+ host = xstrdup(v);
+ }
+ else if (!strcmp(buf, "path"))
+ path = xstrdup(v);
+ else if (!strcmp(buf, "username"))
+ username = xstrdup(v);
+ else if (!strcmp(buf, "password"))
+ password = xstrdup(v);
+ }
+}
+
+int main(int argc, const char **argv)
+{
+ const char *usage =
+ "Usage: git credential-osxkeychain <get|store|erase>";
+
+ if (!argv[1])
+ die(usage);
+
+ read_credential();
+
+ if (!strcmp(argv[1], "get"))
+ find_internet_password();
+ else if (!strcmp(argv[1], "store"))
+ add_internet_password();
+ else if (!strcmp(argv[1], "erase"))
+ delete_internet_password();
+ /* otherwise, ignore unknown action */
+
+ return 0;
+}
--
1.7.8.rc2.40.gaf387
^ permalink raw reply related
* [PATCHv2 9/9] Makefile: OS X has /dev/tty
From: Jeff King @ 2011-12-10 10:41 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <20111210103943.GA16478@sigill.intra.peff.net>
We can use our enhanced getpass(). Tested by me.
Signed-off-by: Jeff King <peff@peff.net>
---
Makefile | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
index 198fc9b..1b3f5f0 100644
--- a/Makefile
+++ b/Makefile
@@ -902,6 +902,7 @@ ifeq ($(uname_S),Darwin)
endif
NO_MEMMEM = YesPlease
USE_ST_TIMESPEC = YesPlease
+ HAVE_DEV_TTY = YesPlease
endif
ifeq ($(uname_S),SunOS)
NEEDS_SOCKET = YesPlease
--
1.7.8.rc2.40.gaf387
^ permalink raw reply related
* [PATCHv2 8/9] Makefile: linux has /dev/tty
From: Jeff King @ 2011-12-10 10:41 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <20111210103943.GA16478@sigill.intra.peff.net>
Therefore we can turn on our custom prompt function instead
of relying on getpass.
Signed-off-by: Jeff King <peff@peff.net>
---
Makefile | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
index 4ef6ba5..198fc9b 100644
--- a/Makefile
+++ b/Makefile
@@ -841,6 +841,7 @@ ifeq ($(uname_S),Linux)
NO_STRLCPY = YesPlease
NO_MKSTEMPS = YesPlease
HAVE_PATHS_H = YesPlease
+ HAVE_DEV_TTY = YesPlease
endif
ifeq ($(uname_S),GNU/kFreeBSD)
NO_STRLCPY = YesPlease
--
1.7.8.rc2.40.gaf387
^ permalink raw reply related
* [PATCHv2 7/9] credential: use git_prompt instead of git_getpass
From: Jeff King @ 2011-12-10 10:41 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <20111210103943.GA16478@sigill.intra.peff.net>
We use git_getpass to retrieve the username and password
from the terminal. However, git_getpass will not echo the
username as the user types. We can fix this by using the
more generic git_prompt, which underlies git_getpass but
lets us specify an "echo" option.
Signed-off-by: Jeff King <peff@peff.net>
---
credential.c | 15 +++++++--------
1 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/credential.c b/credential.c
index fbb7231..62d1c56 100644
--- a/credential.c
+++ b/credential.c
@@ -109,7 +109,8 @@ static void credential_describe(struct credential *c, struct strbuf *out)
strbuf_addf(out, "/%s", c->path);
}
-static char *credential_ask_one(const char *what, struct credential *c)
+static char *credential_ask_one(const char *what, struct credential *c,
+ int flags)
{
struct strbuf desc = STRBUF_INIT;
struct strbuf prompt = STRBUF_INIT;
@@ -121,11 +122,7 @@ static void credential_describe(struct credential *c, struct strbuf *out)
else
strbuf_addf(&prompt, "%s: ", what);
- /* FIXME: for usernames, we should do something less magical that
- * actually echoes the characters. However, we need to read from
- * /dev/tty and not stdio, which is not portable (but getpass will do
- * it for us). http.c uses the same workaround. */
- r = git_getpass(prompt.buf);
+ r = git_prompt(prompt.buf, flags);
strbuf_release(&desc);
strbuf_release(&prompt);
@@ -135,9 +132,11 @@ static void credential_describe(struct credential *c, struct strbuf *out)
static void credential_getpass(struct credential *c)
{
if (!c->username)
- c->username = credential_ask_one("Username", c);
+ c->username = credential_ask_one("Username", c,
+ PROMPT_ASKPASS|PROMPT_ECHO);
if (!c->password)
- c->password = credential_ask_one("Password", c);
+ c->password = credential_ask_one("Password", c,
+ PROMPT_ASKPASS);
}
int credential_read(struct credential *c, FILE *fp)
--
1.7.8.rc2.40.gaf387
^ permalink raw reply related
* [PATCHv2 6/9] prompt: use git_terminal_prompt
From: Jeff King @ 2011-12-10 10:41 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <20111210103943.GA16478@sigill.intra.peff.net>
Our custom implementation of git_terminal_prompt has many
advantages over regular getpass(), as described in the prior
commit.
This also lets us implement a PROMPT_ECHO flag for callers
who want it.
Signed-off-by: Jeff King <peff@peff.net>
---
prompt.c | 3 ++-
prompt.h | 1 +
2 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/prompt.c b/prompt.c
index 2002644..72ab9de 100644
--- a/prompt.c
+++ b/prompt.c
@@ -2,6 +2,7 @@
#include "run-command.h"
#include "strbuf.h"
#include "prompt.h"
+#include "compat/terminal.h"
static char *do_askpass(const char *cmd, const char *prompt)
{
@@ -50,7 +51,7 @@
return do_askpass(askpass, prompt);
}
- r = getpass(prompt);
+ r = git_terminal_prompt(prompt, flags & PROMPT_ECHO);
if (!r)
die_errno("could not read '%s'", prompt);
return r;
diff --git a/prompt.h b/prompt.h
index 9ab85a7..04f321a 100644
--- a/prompt.h
+++ b/prompt.h
@@ -2,6 +2,7 @@
#define PROMPT_H
#define PROMPT_ASKPASS (1<<0)
+#define PROMPT_ECHO (1<<1)
char *git_prompt(const char *prompt, int flags);
char *git_getpass(const char *prompt);
--
1.7.8.rc2.40.gaf387
^ permalink raw reply related
* [PATCHv2 5/9] add generic terminal prompt function
From: Jeff King @ 2011-12-10 10:41 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <20111210103943.GA16478@sigill.intra.peff.net>
When we need to prompt the user for input interactively, we
want to access their terminal directly. We can't rely on
stdio because it may be connected to pipes or files, rather
than the terminal. Instead, we use "getpass()", because it
abstracts the idea of prompting and reading from the
terminal. However, it has some problems:
1. It never echoes the typed characters, which makes it OK
for passwords but annoying for other input (like usernames).
2. Some implementations of getpass() have an extremely
small input buffer (e.g., Solaris 8 is reported to
support only 8 characters).
3. Some implementations of getpass() will fall back to
reading from stdin (e.g., glibc). We explicitly don't
want this, because our stdin may be connected to a pipe
speaking a particular protocol, and reading will
disrupt the protocol flow (e.g., the remote-curl
helper).
4. Some implementations of getpass() turn off signals, so
that hitting "^C" on the terminal does not break out of
the password prompt. This can be a mild annoyance.
Instead, let's provide an abstract "git_terminal_prompt"
function that addresses these concerns. This patch includes
an implementation based on /dev/tty, enabled by setting
HAVE_DEV_TTY. The fallback is to use getpass() as before.
Signed-off-by: Jeff King <peff@peff.net>
---
Makefile | 9 ++++++
compat/terminal.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++
compat/terminal.h | 6 ++++
3 files changed, 96 insertions(+), 0 deletions(-)
create mode 100644 compat/terminal.c
create mode 100644 compat/terminal.h
diff --git a/Makefile b/Makefile
index 5201bea..4ef6ba5 100644
--- a/Makefile
+++ b/Makefile
@@ -227,6 +227,9 @@ all::
#
# Define NO_REGEX if you have no or inferior regex support in your C library.
#
+# Define HAVE_DEV_TTY if your system can open /dev/tty to interact with the
+# user.
+#
# Define GETTEXT_POISON if you are debugging the choice of strings marked
# for translation. In a GETTEXT_POISON build, you can turn all strings marked
# for translation into gibberish by setting the GIT_GETTEXT_POISON variable
@@ -523,6 +526,7 @@ LIB_H += compat/bswap.h
LIB_H += compat/cygwin.h
LIB_H += compat/mingw.h
LIB_H += compat/obstack.h
+LIB_H += compat/terminal.h
LIB_H += compat/win32/pthread.h
LIB_H += compat/win32/syslog.h
LIB_H += compat/win32/poll.h
@@ -612,6 +616,7 @@ LIB_OBJS += color.o
LIB_OBJS += combine-diff.o
LIB_OBJS += commit.o
LIB_OBJS += compat/obstack.o
+LIB_OBJS += compat/terminal.o
LIB_OBJS += config.o
LIB_OBJS += connect.o
LIB_OBJS += connected.o
@@ -1642,6 +1647,10 @@ ifdef HAVE_PATHS_H
BASIC_CFLAGS += -DHAVE_PATHS_H
endif
+ifdef HAVE_DEV_TTY
+ BASIC_CFLAGS += -DHAVE_DEV_TTY
+endif
+
ifdef DIR_HAS_BSD_GROUP_SEMANTICS
COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS
endif
diff --git a/compat/terminal.c b/compat/terminal.c
new file mode 100644
index 0000000..6d16c8f
--- /dev/null
+++ b/compat/terminal.c
@@ -0,0 +1,81 @@
+#include "git-compat-util.h"
+#include "compat/terminal.h"
+#include "sigchain.h"
+#include "strbuf.h"
+
+#ifdef HAVE_DEV_TTY
+
+static int term_fd = -1;
+static struct termios old_term;
+
+static void restore_term(void)
+{
+ if (term_fd < 0)
+ return;
+
+ tcsetattr(term_fd, TCSAFLUSH, &old_term);
+ term_fd = -1;
+}
+
+static void restore_term_on_signal(int sig)
+{
+ restore_term();
+ sigchain_pop(sig);
+ raise(sig);
+}
+
+char *git_terminal_prompt(const char *prompt, int echo)
+{
+ static struct strbuf buf = STRBUF_INIT;
+ int r;
+ FILE *fh;
+
+ fh = fopen("/dev/tty", "w+");
+ if (!fh)
+ return NULL;
+
+ if (!echo) {
+ struct termios t;
+
+ if (tcgetattr(fileno(fh), &t) < 0) {
+ fclose(fh);
+ return NULL;
+ }
+
+ old_term = t;
+ term_fd = fileno(fh);
+ sigchain_push_common(restore_term_on_signal);
+
+ t.c_lflag &= ~ECHO;
+ if (tcsetattr(fileno(fh), TCSAFLUSH, &t) < 0) {
+ term_fd = -1;
+ fclose(fh);
+ return NULL;
+ }
+ }
+
+ fputs(prompt, fh);
+ fflush(fh);
+
+ r = strbuf_getline(&buf, fh, '\n');
+ if (!echo) {
+ putc('\n', fh);
+ fflush(fh);
+ }
+
+ restore_term();
+ fclose(fh);
+
+ if (r == EOF)
+ return NULL;
+ return buf.buf;
+}
+
+#else
+
+char *git_terminal_prompt(const char *prompt, int echo)
+{
+ return getpass(prompt);
+}
+
+#endif
diff --git a/compat/terminal.h b/compat/terminal.h
new file mode 100644
index 0000000..97db7cd
--- /dev/null
+++ b/compat/terminal.h
@@ -0,0 +1,6 @@
+#ifndef COMPAT_TERMINAL_H
+#define COMPAT_TERMINAL_H
+
+char *git_terminal_prompt(const char *prompt, int echo);
+
+#endif /* COMPAT_TERMINAL_H */
--
1.7.8.rc2.40.gaf387
^ permalink raw reply related
* [PATCHv2 4/9] refactor git_getpass into generic prompt function
From: Jeff King @ 2011-12-10 10:40 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <20111210103943.GA16478@sigill.intra.peff.net>
This will allow callers to specify more options (e.g.,
leaving echo on). The original git_getpass becomes a slim
wrapper around the new function.
Signed-off-by: Jeff King <peff@peff.net>
---
prompt.c | 46 ++++++++++++++++++++++++++++++----------------
prompt.h | 3 +++
2 files changed, 33 insertions(+), 16 deletions(-)
diff --git a/prompt.c b/prompt.c
index 42a1c9f..2002644 100644
--- a/prompt.c
+++ b/prompt.c
@@ -3,26 +3,13 @@
#include "strbuf.h"
#include "prompt.h"
-char *git_getpass(const char *prompt)
+static char *do_askpass(const char *cmd, const char *prompt)
{
- const char *askpass;
struct child_process pass;
const char *args[3];
static struct strbuf buffer = STRBUF_INIT;
- askpass = getenv("GIT_ASKPASS");
- if (!askpass)
- askpass = askpass_program;
- if (!askpass)
- askpass = getenv("SSH_ASKPASS");
- if (!askpass || !(*askpass)) {
- char *result = getpass(prompt);
- if (!result)
- die_errno("Could not read password");
- return result;
- }
-
- args[0] = askpass;
+ args[0] = cmd;
args[1] = prompt;
args[2] = NULL;
@@ -35,7 +22,7 @@
strbuf_reset(&buffer);
if (strbuf_read(&buffer, pass.out, 20) < 0)
- die("failed to read password from %s\n", askpass);
+ die("failed to get '%s' from %s\n", prompt, cmd);
close(pass.out);
@@ -46,3 +33,30 @@
return buffer.buf;
}
+
+char *git_prompt(const char *prompt, int flags)
+{
+ char *r;
+
+ if (flags & PROMPT_ASKPASS) {
+ const char *askpass;
+
+ askpass = getenv("GIT_ASKPASS");
+ if (!askpass)
+ askpass = askpass_program;
+ if (!askpass)
+ askpass = getenv("SSH_ASKPASS");
+ if (askpass && *askpass)
+ return do_askpass(askpass, prompt);
+ }
+
+ r = getpass(prompt);
+ if (!r)
+ die_errno("could not read '%s'", prompt);
+ return r;
+}
+
+char *git_getpass(const char *prompt)
+{
+ return git_prompt(prompt, PROMPT_ASKPASS);
+}
diff --git a/prompt.h b/prompt.h
index 0fd7bd9..9ab85a7 100644
--- a/prompt.h
+++ b/prompt.h
@@ -1,6 +1,9 @@
#ifndef PROMPT_H
#define PROMPT_H
+#define PROMPT_ASKPASS (1<<0)
+
+char *git_prompt(const char *prompt, int flags);
char *git_getpass(const char *prompt);
#endif /* PROMPT_H */
--
1.7.8.rc2.40.gaf387
^ permalink raw reply related
* [PATCHv2 3/9] move git_getpass to its own source file
From: Jeff King @ 2011-12-10 10:40 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <20111210103943.GA16478@sigill.intra.peff.net>
This is currently in connect.c, but really has nothing to
do with the git protocol itself. Let's make a new source
file all about prompting the user, which will make it
cleaner to refactor.
Signed-off-by: Jeff King <peff@peff.net>
---
Makefile | 2 ++
cache.h | 1 -
connect.c | 44 --------------------------------------------
credential.c | 1 +
imap-send.c | 1 +
prompt.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
prompt.h | 6 ++++++
7 files changed, 58 insertions(+), 45 deletions(-)
create mode 100644 prompt.c
create mode 100644 prompt.h
diff --git a/Makefile b/Makefile
index b144f1a..5201bea 100644
--- a/Makefile
+++ b/Makefile
@@ -565,6 +565,7 @@ LIB_H += parse-options.h
LIB_H += patch-ids.h
LIB_H += pkt-line.h
LIB_H += progress.h
+LIB_H += prompt.h
LIB_H += quote.h
LIB_H += reflog-walk.h
LIB_H += refs.h
@@ -672,6 +673,7 @@ LIB_OBJS += pkt-line.o
LIB_OBJS += preload-index.o
LIB_OBJS += pretty.o
LIB_OBJS += progress.o
+LIB_OBJS += prompt.o
LIB_OBJS += quote.o
LIB_OBJS += reachable.o
LIB_OBJS += read-cache.o
diff --git a/cache.h b/cache.h
index 8c98d05..bb26c2f 100644
--- a/cache.h
+++ b/cache.h
@@ -1025,7 +1025,6 @@ struct ref {
extern struct ref *find_ref_by_name(const struct ref *list, const char *name);
#define CONNECT_VERBOSE (1u << 0)
-extern char *git_getpass(const char *prompt);
extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
extern int finish_connect(struct child_process *conn);
extern int git_connection_is_socket(struct child_process *conn);
diff --git a/connect.c b/connect.c
index 51990fa..519e527 100644
--- a/connect.c
+++ b/connect.c
@@ -619,47 +619,3 @@ int finish_connect(struct child_process *conn)
free(conn);
return code;
}
-
-char *git_getpass(const char *prompt)
-{
- const char *askpass;
- struct child_process pass;
- const char *args[3];
- static struct strbuf buffer = STRBUF_INIT;
-
- askpass = getenv("GIT_ASKPASS");
- if (!askpass)
- askpass = askpass_program;
- if (!askpass)
- askpass = getenv("SSH_ASKPASS");
- if (!askpass || !(*askpass)) {
- char *result = getpass(prompt);
- if (!result)
- die_errno("Could not read password");
- return result;
- }
-
- args[0] = askpass;
- args[1] = prompt;
- args[2] = NULL;
-
- memset(&pass, 0, sizeof(pass));
- pass.argv = args;
- pass.out = -1;
-
- if (start_command(&pass))
- exit(1);
-
- strbuf_reset(&buffer);
- if (strbuf_read(&buffer, pass.out, 20) < 0)
- die("failed to read password from %s\n", askpass);
-
- close(pass.out);
-
- if (finish_command(&pass))
- exit(1);
-
- strbuf_setlen(&buffer, strcspn(buffer.buf, "\r\n"));
-
- return buffer.buf;
-}
diff --git a/credential.c b/credential.c
index a17eafe..fbb7231 100644
--- a/credential.c
+++ b/credential.c
@@ -3,6 +3,7 @@
#include "string-list.h"
#include "run-command.h"
#include "url.h"
+#include "prompt.h"
void credential_init(struct credential *c)
{
diff --git a/imap-send.c b/imap-send.c
index 227253e..43588e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -25,6 +25,7 @@
#include "cache.h"
#include "exec_cmd.h"
#include "run-command.h"
+#include "prompt.h"
#ifdef NO_OPENSSL
typedef void *SSL;
#else
diff --git a/prompt.c b/prompt.c
new file mode 100644
index 0000000..42a1c9f
--- /dev/null
+++ b/prompt.c
@@ -0,0 +1,48 @@
+#include "cache.h"
+#include "run-command.h"
+#include "strbuf.h"
+#include "prompt.h"
+
+char *git_getpass(const char *prompt)
+{
+ const char *askpass;
+ struct child_process pass;
+ const char *args[3];
+ static struct strbuf buffer = STRBUF_INIT;
+
+ askpass = getenv("GIT_ASKPASS");
+ if (!askpass)
+ askpass = askpass_program;
+ if (!askpass)
+ askpass = getenv("SSH_ASKPASS");
+ if (!askpass || !(*askpass)) {
+ char *result = getpass(prompt);
+ if (!result)
+ die_errno("Could not read password");
+ return result;
+ }
+
+ args[0] = askpass;
+ args[1] = prompt;
+ args[2] = NULL;
+
+ memset(&pass, 0, sizeof(pass));
+ pass.argv = args;
+ pass.out = -1;
+
+ if (start_command(&pass))
+ exit(1);
+
+ strbuf_reset(&buffer);
+ if (strbuf_read(&buffer, pass.out, 20) < 0)
+ die("failed to read password from %s\n", askpass);
+
+ close(pass.out);
+
+ if (finish_command(&pass))
+ exit(1);
+
+ strbuf_setlen(&buffer, strcspn(buffer.buf, "\r\n"));
+
+ return buffer.buf;
+}
diff --git a/prompt.h b/prompt.h
new file mode 100644
index 0000000..0fd7bd9
--- /dev/null
+++ b/prompt.h
@@ -0,0 +1,6 @@
+#ifndef PROMPT_H
+#define PROMPT_H
+
+char *git_getpass(const char *prompt);
+
+#endif /* PROMPT_H */
--
1.7.8.rc2.40.gaf387
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox