0

I am trying to implement Git cherry pick for multiple commit Ids at once. But it fails, or either this is not how it should be used.

I am trying to use bash command from here : https://stackoverflow.com/a/2553705/9614476

  #!/bin/bash

  if [ -z $1 ]; then
      echo "Equivalent to running git-cherry-pick on each of the commits in the range specified.";
      echo "";
      echo "Usage:  $0 start^..end";
      echo "";
      exit 1;
  fi

  git rev-list --reverse --topo-order $1 | while read rev
  do
    git cherry-pick $rev || break
  done

I created a target for ant like this :

<target name="git_multi_cherry_pick">
    <echo message="START: MultiMerge"/>
    <exec executable="C:\Program Files\Git\bin\bash.exe" osfamily="windows" failonerror="true">
        <arg value="${gitMultiCherryPick}"/>
        <arg value="${gitCommitIds}"/>
    </exec>
    <exec executable="/bin/bash" osfamily="unix" failonerror="true">
        <arg value="${gitMultiCherryPick}"/>
        <arg value="${gitCommitIds}"/>
    </exec>
</target>

And I am using AntExecutor from Java to call the above target. AntExecutor code can be found here - https://github.com/asciidoctor/asciidoctor-ant/blob/master/src/test/java/org/asciidoctor/ant/AntExecutor.java

        Map<String, String> propertiesMap = new HashMap<>();
        propertiesMap.put("gitMultiCherryPick", "git-multi-cherry-pick.sh");
        propertiesMap.put("gitCommitIds", "204a3e81712000d09a2794cc4ef1d090c4280f4e,9c5242d6a15b14032e9fc1f408be3c91026b1e1f");
        List<String> sf_build = AntExecutor.executeAntTask("FullPath \\resources\\build\\build.xml",
                "git_multi_cherry_pick", propertiesMap);

But it fails with this error :

The ' characters around the executable and arguments are not part of the command., 204a3e81712000d09a2794cc4ef1d090c4280f4e,9c5242d6a15b14032e9fc1f408be3c91026b1e1f, git-multi-cherry-pick.sh, fatal: ambiguous argument '204a3e81712000d09a2794cc4ef1d090c4280f4e,9c5242d6a15b14032e9fc1f408be3c91026b1e1f':

I think that is not how we pass the commitIds. Has anyone used the above bash script before?

Nagendra Singh
  • 577
  • 1
  • 7
  • 24
  • 1
    IIRC `git cherry-pick` can already handle several commits at once. Having said that, it sounds like you are actually looking for `git rebase`, no? – knittl Apr 05 '20 at 17:44
  • So my requirement is something like this , Take multiple commit Ids as input and create a branch out of it, the first commit ID being the base, and the next commit ids to be merged into that base. – Nagendra Singh Apr 05 '20 at 17:47
  • 1
    It still sounds suspiciously like rebasing: `git checkout -b your_branch end_commit; git rebase --onto target_commit starting_commit your_branch;` (comparable to `git checkout -b your_branch target_commit; git cherry_pick starting_commit..end_commit;` – knittl Apr 05 '20 at 17:49
  • So here is the catch, the `target_commit starting_commit` can be in mixed way. i.e. it may not be consecutive commit ids, it can be like if there are commit ids like 1,2,3,4,5 then I would like to only commit 2 and 4. So I would have to commit in loop, right? – Nagendra Singh Apr 05 '20 at 17:52
  • Yes, but your script performs a `rev-list`, so you would get all commits between those two commits already. Without a range, you would apply all commits – knittl Apr 05 '20 at 17:55
  • Ok, I see that now. So is there a way to do that using `bash` or do I need to write it fully custom? – Nagendra Singh Apr 05 '20 at 18:01
  • @NagendraSingh, you are being suggested to use `git rebase`. Please, read the man page of `git rebase`. What you try to do is a rebase of your branch (complete) to another place. Look at the doc. – Luis Colorado Apr 06 '20 at 08:39

1 Answers1

0

I think you are actually looking for git rebase, not git cherry-pick (both of which can perform the same work). git cherry-pick in recent Git release can even process multiple commits directly, without the need for an external loop.

To address your specific question: when specifying several commit refs on the command line, those need to be separated by your shells IFS (input field separator), which is usually whitespace and rarely a comma (,).

Possible solutions:

  • Change your map value to 204a3e81712000d09a2794cc4ef1d090c4280f4e 9c5242d6a15b14032e9fc1f408be3c91026b1e1f (pay attention to quoting in your script)
  • Set IFS in your script to ','

git cherry-pick can already pick multiple commits at once (see documentation). You still need to fix the value in your map from comma to blank, but then you could simplify your shell script.

This also fixes the problem of using rev-list without a range, but only 2 starting points (abc123 def456 instead of abc123..def456). It would traverse all of your history, not only commits between your two refs. Drop the rev-list and utilize cherry-pick directly:

#!/bin/sh

if [ -z "$1" ]; then
    echo "Equivalent to running git-cherry-pick on each of the commits in the range specified.";
    echo "";
    echo "Usage: $0 start^..end";
    echo "";
    exit 1;
fi


# do not quote parameter, we need potential whitespace preserved
git cherry-pick $1

(and you do not need bash, sh will totally suffice for a script like this)

knittl
  • 246,190
  • 53
  • 318
  • 364
  • After giving space now it gives `The ' characters around the executable and arguments are not part of the command., fatal: bad object 204a3e81712000d09a2794cc4ef1d090c4280f4e, exec returned: 128]` bad object error. – Nagendra Singh Apr 05 '20 at 18:08
  • I ran this command `git show 204a3e81712000d09a2794cc4ef1d090c4280f4e` and its running fine, but still bad object error is pretty weird. – Nagendra Singh Apr 05 '20 at 18:15
  • @NagendraSingh the error message still contains the `,` comma character that should be removed. – knittl Apr 05 '20 at 18:23
  • I changed my map to `propertiesMap.put("gitCommitIds", "204a3e81712000d09a2794cc4ef1d090c4280f4e f48b8cfeb852495489b6198d6c73eef3f5f53d9e");` – Nagendra Singh Apr 05 '20 at 18:24
  • Are you sure? The error message contains »`fatal: bad object …80f4e,`. But I'm not sure if Ant is being funny here. I'm not sure how it is passing its parameters. Maybe it is already escaping or quoting its arguments. Do you expect N commits or only 2 at most? if only 2, I suggest defining separate arguments for them and putting them into a single value – knittl Apr 05 '20 at 18:28
  • Yes, I can paste the ant logs here : `The ' characters around the executable and arguments are not part of the command., Execute:Java13CommandLauncher: Executing 'C:\Program Files\Git\bin\bash.exe' with arguments: 'git-multi-cherry-pick.sh' '204a3e81712000d09a2794cc4ef1d090c4280f4e f48b8cfeb852495489b6198d6c73eef3f5f53d9e' The ' characters around the executable and arguments are not part of the command., Start Cherry Pick, fatal: bad object 204a3e81712000d09a2794cc4ef1d090c4280f4e, exec returned: 128]` – Nagendra Singh Apr 05 '20 at 18:31
  • @NagendraSingh Can you try with a single commit only? Can you check if the working directory is set correctly when executing the git command (add `pwd` and `git rev-parse --absolute-git-dir`)? What happens if you run `git show` from within your script? – knittl Apr 05 '20 at 18:43
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/211017/discussion-between-nagendra-singh-and-knittl). – Nagendra Singh Apr 05 '20 at 19:00
  • As per your suggestion I changed the `cherry-pick` to `rebase`, but how do I validate this: `#!/bin/bash echo 'Start Cherry Pick' cd $4 || exit echo 'Switched to '$4 git clone repoURL.git . git checkout -f origin/master git checkout -b TestCEAS 212e4a80dd8638af35aeaf36776bffc4cda85df5 git rebase --onto 212e4a80dd8638af35aeaf36776bffc4cda85df5 a0b21246fec005f89fbf8fa5051098981bc4e69a TestCEAS` – Nagendra Singh Apr 06 '20 at 07:23
  • But how do I verify if the new branch `TestCEAS` has only commits range given in `212e4a80dd8638af35aeaf36776bffc4cda85df5 a0b21246fec005f89fbf8fa5051098981bc4e69a` – Nagendra Singh Apr 06 '20 at 07:36
  • Also when you wrote this `git checkout -b your_branch end_commit; git rebase --onto target_commit starting_commit your_branch;`, end_commit means -> last commit id and starting_commit means -> First Commit Id, but what is 'target_commit', is this the last commit id too? – Nagendra Singh Apr 06 '20 at 07:43
  • So my doubt is why not just do `git checkout -b your_branch end_commit;` as it will extract all the commit till that commit id. Why is this required `git rebase --onto target_commit starting_commit your_branch;` and if its required then what is difference between `end_commit` and `target_commit`. – Nagendra Singh Apr 06 '20 at 09:33
  • I thought we already agreed that `rebase` will not work, because you are trying to pick individual commits that are not all directly linked (IOW your commit graph contains "gaps"). Test your script without Ant first to pinpoint your actual problem. If the script works, see if Ant is starting it incorrectly – knittl Apr 06 '20 at 10:33
  • I changed my approach now, so user can only select start commit and end commit ids from a branch, and then proceed further. – Nagendra Singh Apr 06 '20 at 10:35
  • I see. A totally different question then. But nevermind. `your_branch` is the name of the branch which should eventually contain all your commits. `target_commit` is where your changes should be applied to. `start_commit` is the commit from where to start. It is _excluded_ from your result (use `start_commit^` to include it). `end_commit` is the last commit that should be in your resulting history – knittl Apr 06 '20 at 10:46