git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH/RFC] git-post: the opposite of git-cherry-pick

From
Johannes Sixt <j6t@kdbg.org>
Date
Oct 5, 2017, 19:13 UTC
Message-ID
<c6b52120-98bf-d685-6dc0-3c83e9e80d30@kdbg.org>

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
Next: Stefan Beller
Message 1 of 8 in “git-post: the opposite of git-cherry-pick”
  1. git-post: the opposite of git-cherry-pickJohannes Sixt, Oct 5, 2017
  2. Stefan BellerOct 5, 2017
  3. Johannes SixtOct 5, 2017
  4. Ævar Arnfjörð BjarmasonOct 13, 2017
  5. Johannes SixtOct 15, 2017
  6. Rafael AscensaoOct 16, 2017
  7. Johannes SixtOct 17, 2017
  8. Igor DjordjevicOct 17, 2017

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.