git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Johannes Sixt <j6t@kdbg.org>
To: Git Mailing List <git@vger.kernel.org>
Subject: [PATCH/RFC] git-post: the opposite of git-cherry-pick
Date: Thu, 5 Oct 2017 21:13:29 +0200	[thread overview]
Message-ID: <c6b52120-98bf-d685-6dc0-3c83e9e80d30@kdbg.org> (raw)

Add a new command that can be used to copy an arbitrary commit
to a branch that is not checked out.

Signed-off-by: Johannes Sixt <j6t@kdbg.org>
---
 I have been using this command since years, but got around to
 write a man page and tests only now. I hope the man page makes
 sense. I don't have the tool chain to build it, though, so
 any hints for improvement are welcome.

 .gitignore                        |  1 +
 Documentation/git-cherry-pick.txt |  3 +-
 Documentation/git-post.txt        | 57 +++++++++++++++++++++++++++++
 Makefile                          |  1 +
 command-list.txt                  |  1 +
 git-post.sh                       | 60 ++++++++++++++++++++++++++++++
 t/t3514-post.sh                   | 77 +++++++++++++++++++++++++++++++++++++++
 7 files changed, 199 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/git-post.txt
 create mode 100755 git-post.sh
 create mode 100755 t/t3514-post.sh

diff --git a/.gitignore b/.gitignore
index 833ef3b0b7..a16263249a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -106,6 +106,7 @@
 /git-pack-refs
 /git-parse-remote
 /git-patch-id
+/git-post
 /git-prune
 /git-prune-packed
 /git-pull
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index d35d771fc8..6b34f4d994 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -226,7 +226,8 @@ context lines.
 
 SEE ALSO
 --------
-linkgit:git-revert[1]
+linkgit:git-revert[1],
+linkgit:git-post[1]
 
 GIT
 ---
diff --git a/Documentation/git-post.txt b/Documentation/git-post.txt
new file mode 100644
index 0000000000..e835e62be3
--- /dev/null
+++ b/Documentation/git-post.txt
@@ -0,0 +1,57 @@
+git-post(1)
+===========
+
+NAME
+----
+git-post - Apply a commit on top of a branch that is not checked out
+
+SYNOPSIS
+--------
+[verse]
+'git post' dest-branch [source-rev]
+
+DESCRIPTION
+-----------
+
+Applies the changes made by 'source-rev' (or, if not given, `HEAD`)
+on top of the branch 'dest-branch' and records a new commit.
+'dest-branch' is advanced to point to the new commit.
+The operation that this command performs can be regarded as
+the opposite of cherry-picking.
+
+EXAMPLES
+--------
+
+Assume, while working on a topic, you find and fix an unrelated bug.
+Now:
+
+------------
+$ git commit                                   <1>
+$ git post master                              <2>
+$ git show | git apply -R && git reset HEAD^   <3>
+------------
+
+<1> create a commit with the fix on the current branch
+<2> copy the fix onto the branch where it ought to be
+<3> revert current topic branch to the unfixed state;
+can also be done with `git reset --keep HEAD^` if there are no
+unstaged changes in files that are modified by the fix
+
+Oftentimes, switching branches triggers a rebuild of a code base.
+With the sequence above the branch switch can be avoided.
+That said, it is good practice to test the bug fix on the
+destination branch eventually.
+
+BUGS
+----
+
+The change can be applied on `dest-branch` only if there is
+no textual conflict.
+
+SEE ALSO
+--------
+linkgit:git-cherry-pick[1].
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Makefile b/Makefile
index b143e4eea3..1753bf176f 100644
--- a/Makefile
+++ b/Makefile
@@ -551,6 +551,7 @@ SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
 SCRIPT_SH += git-mergetool.sh
+SCRIPT_SH += git-post.sh
 SCRIPT_SH += git-quiltimport.sh
 SCRIPT_SH += git-rebase.sh
 SCRIPT_SH += git-remote-testgit.sh
diff --git a/command-list.txt b/command-list.txt
index a1fad28fd8..bc95b424d0 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -98,6 +98,7 @@ git-pack-redundant                      plumbinginterrogators
 git-pack-refs                           ancillarymanipulators
 git-parse-remote                        synchelpers
 git-patch-id                            purehelpers
+git-post                                mainporcelain
 git-prune                               ancillarymanipulators
 git-prune-packed                        plumbingmanipulators
 git-pull                                mainporcelain           remote
diff --git a/git-post.sh b/git-post.sh
new file mode 100755
index 0000000000..6627d69f73
--- /dev/null
+++ b/git-post.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+# Copyright (c) 2017 Johannes Sixt
+
+SUBDIRECTORY_OK=Yes
+OPTIONS_SPEC="\
+git post dest-branch [source-rev]
+--
+"
+. git-sh-setup
+
+while test $# != 0
+do
+	case "$1" in
+	--)	shift; break;;
+	-*)	usage;;
+	*)	break;;
+	esac
+	shift
+done
+
+dest=$(git rev-parse --verify --symbolic-full-name "$1") || exit
+if test -z "$dest"
+then
+	die "$(gettext "Destination must be a branch tip")"
+fi
+
+shift
+case $# in
+0)	set -- HEAD;;
+1)	: good;;
+*)	usage;;
+esac
+
+# apply change to a temporary index
+tmpidx=$GIT_DIR/index-post-$$
+git read-tree --index-output="$tmpidx" "$dest" || exit
+GIT_INDEX_FILE=$tmpidx
+export GIT_INDEX_FILE
+trap 'rm -f "$tmpidx"' 0 1 2 15
+
+git diff-tree -p --binary -M -C "$1" | git apply --cached || exit
+
+newtree=$(git write-tree) &&
+newrev=$(
+	eval "$(get_author_ident_from_commit "$1")" &&
+	export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
+	git-cat-file commit "$1" | sed -e '1,/^$/d' |
+	git commit-tree $newtree -p "$dest"
+) || exit
+
+if git check-ref-format "$dest"
+then
+	set_reflog_action post
+	subject=$(git log --no-walk --pretty=%s "$newrev") &&
+	git update-ref -m "$GIT_REFLOG_ACTION: $subject" "$dest" "$newrev" || exit
+fi
+if test -z "$GIT_QUIET"
+then
+	git rev-list -1 --oneline "$newrev"
+fi
diff --git a/t/t3514-post.sh b/t/t3514-post.sh
new file mode 100755
index 0000000000..4d31515c52
--- /dev/null
+++ b/t/t3514-post.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+test_description='test git post
+
+We build this history:
+
+   A--B--C  <-- master, HEAD
+  /
+ O          <-- side-base, side
+
+Then we post B and C on top of branch "side":
+
+   A--B--C  <-- master, HEAD
+  /
+ O          <-- side-base
+  \
+   B*--C*   <-- side
+
+B has a different author, which must be copied to B*.
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	printf "a%s\n" 1 2 3 4 >file-a &&
+	printf "b%s\n" 5 6 7 8 >file-b &&
+
+	test_tick &&
+	git add file-a file-b &&
+	git commit -m initial &&
+	git tag side-base &&
+
+	test_tick &&
+	echo "Advance master" >>file-a &&
+	git commit -a -m advance-master &&
+
+	test_tick &&
+	echo "Unrelated fix" >>file-b &&
+	GIT_AUTHOR_NAME="S O Else" git commit -a -m fix-for-b &&
+
+	test_tick &&
+	echo "Another fix" >>file-b &&
+	git commit -a -m another-fix-for-b
+'
+
+test_expect_success 'post two commits on top of side' '
+
+	git branch -f side side-base &&
+	test_tick &&
+	git post side HEAD^ &&
+	test_tick &&
+	git post side &&
+
+	git log --pretty="%at %an %ae %s" HEAD~2.. >expect &&
+	git log --pretty="%at %an %ae %s" side-base..side >actual &&
+
+	test_cmp expect actual &&
+	git cat-file blob side:file-b >actual &&
+	test_cmp file-b actual &&
+
+	git diff --exit-code side-base side -- file-a	# no change
+'
+
+test_expect_success 'post requiring merge resolution fails' '
+
+	git branch -f side side-base &&
+	test_must_fail git post side HEAD
+'
+
+test_expect_success 'cannot post onto arbitrary commit name' '
+
+	git branch -f side side-base &&
+	test_must_fail git post side^0 HEAD^
+'
+
+test_done
-- 
2.14.2.808.g3bc32f2729

             reply	other threads:[~2017-10-05 19:13 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-10-05 19:13 Johannes Sixt [this message]
2017-10-05 19:33 ` [PATCH/RFC] git-post: the opposite of git-cherry-pick Stefan Beller
2017-10-05 21:22   ` Johannes Sixt
2017-10-13 10:51     ` Ævar Arnfjörð Bjarmason
2017-10-15 15:09       ` Johannes Sixt
2017-10-16 23:01         ` Rafael Ascensao
2017-10-17 17:30           ` Johannes Sixt
2017-10-17 21:15             ` Igor Djordjevic

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=c6b52120-98bf-d685-6dc0-3c83e9e80d30@kdbg.org \
    --to=j6t@kdbg.org \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).