2

I have these commits: (TL;DR below...)

A <- B <- C

These are on top of colleague's commits:

U <- V <- W <- A <- B <- C

Now the colleague rebased his branch to new master, pushed to github repo which updated the PR then added a commit, pushed to github repo, then I resolved the conflicts on github and commited to the branch on github, then the colleague squashed two of the commits and added another one, and then our team lead chimed in and starts editing with github editor. So it ends up like this: [1]

X' <- A'' <- U -> C <- W -> B ---> L --> O --> O  
<--- master'      /    |                      /   <------ master
G ---------> M --/      \- abyssmal' <-- P --/

In other words, the original branches got quite irrelevant and the new state of things has very little to do with the original state. I think no git command manipulating the branches can really target this using . I only want to take the remnants of my work (in the form of few successive commits) and try to adapt it to today's codebase.

I am writing the story because typically people try to offer solutions to my underlying problem, which is great, but I would prefer to know this particular operation, if github has means to do it. Also, I am stating that there are no branches because whenever few people work on one branch, things get messy quickly and branches loose any sense. Lack of discipline or using Git in a wrong way. I know. Not solving that, I will be happy with the solution to this.

All that matters to me is:


TL;DR:

How to copy any chain of commits (provided they are chained) to some other commit, in one command? I.e. I have:

...hicSuntLeones <- myCommitA <- myCommitB <- myCommitC <- whatever...

I want a super-simple command:

git copyCommits --since myCommitA --until myCommitC --putThemTo terraInrecognita

I want it to end up like this:

...terraIncognita <- myCommitA' <- myCommitB' <- myCommitC'

With intermediate conflicts resolving of course.


I was looking at git rebase --onto, but that seems to work with different logic that I need - it would let me "divorce" another branch, but I don't have that branch. If I understand it correctly.

I am looking around stack overflow and web, but still can't find. The git answers tend to explain how git works, how git rebase works, what is the contrast of the design philosophy of git with the philosophy of a more traditional source control tool like svn, how SCM works in general, how the relationships with parents are important and other things. I don't want to sound arrogant, but I am only looking for a command to do the above. Is there any? Thanks :)

[1] It's not really that bad, just normal daily chaos. But still it makes this approach easier than thinking in branches.

Edit: Not providing an answer: Difference between 'rebase master' and 'rebase --onto master' from a branch derived from a branch of master This referenced duplicate, also referenced in the question, does not answer my question. It has lenghty answers on how git works, how branches work, but not really giving the answer to my question. Maybe it's implied there but not given.

Community
  • 1
  • 1
Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
  • What about `git --onto terraIncognita myCommitA^ myCommitC`? Isn't that just enough? Being `myCommitC` the name of a branch, of course. – rodrigo Feb 06 '17 at 22:57
  • There are no branches, only commits. I could perhaps create temporary branches. Or maybe it could take hashes? – Ondra Žižka Feb 06 '17 at 23:04
  • yes, this also takes hashes. – amenthes Feb 06 '17 at 23:04
  • @OndraŽižka: Well, you need a branch anyway if you want to do anything after replaying those commits. So, yes, I would create a local branch first on `myCommitC` and then try the `rebase`. – rodrigo Feb 06 '17 at 23:05
  • Possible duplicate of [Difference between 'rebase master' and 'rebase --onto master' from a branch derived from a branch of master](http://stackoverflow.com/questions/33942588/difference-between-rebase-master-and-rebase-onto-master-from-a-branch-deri) – mkrieger1 Feb 06 '17 at 23:06
  • The referenced duplicate, also referenced in the question, does not answer my question. It has lenghty answers on how git works, how branches work, but not really giving the answer to my question. Maybe it's implied there but not given. – Ondra Žižka Feb 07 '17 at 00:16
  • 1
    It explains how `git rebase --onto` works, which should be what you want if I understand correctly. Have you tried the command suggested by rodrigo? Another variation would be `git rebase --onto terraIncognita hicSuntLeones myCommitC`. – mkrieger1 Feb 07 '17 at 00:24
  • Yep, @rodrigo's way is what seems to do what I need. Ready to accept as an answer. The referenced Q&A explain --onto, but not how to use it to achieve what is being asked here. – Ondra Žižka Feb 07 '17 at 00:43

2 Answers2

2

What you want is simply git cherry-pick, which can pick a chain of commits.

It will take two commands (unless you write your own script) as you need to git checkout the place you want the copies to land. Then, if the first commit to copy is S (for start) and the last is E (for end):

git cherry-pick S^..E

You need the hat-suffix to make the range include commit S itself.

torek
  • 448,244
  • 59
  • 642
  • 775
  • I've tried `cherry-pick` but that 1) squashed the commits to one, 2) left out the last commit for reasons unknown to me. Last, not first. – Ondra Žižka Feb 07 '17 at 00:14
  • 1
    `git cherry-pick` should make individual commits from each copied commit unless you use `-n`. (Note that if you get conflicts, Git will advise you to fix the conflict and commit if necessary, then use `git cherry-pick --continue` to proceed with the remaining commits if any. I should also mention that the ability to pick multiple commits was new in a very old version of Git—1.7.2—so if your Git is even older, you might not have it.) – torek Feb 07 '17 at 00:29
  • Just tried again. I have 4 commits to move: d5c22cb, 7c01600d, ce0d010, 088eaa67 Did `git co terraIncognita; git cherry-pick d5c22cb^..088eaa67`. Resolved conflicts, added, `--continue`d. Ended up with 2 last commits, missing the changes in first two. (My 1s comment wrong on this.) Not sure if I am doing something wrong? – Ondra Žižka Feb 07 '17 at 00:50
  • 1
    It might be helpful to use `git log --graph -n 4 --oneline 088eaa67` to verify that those four commits are directly in sequence, or `git log d5c22cb^..088eaa67`. It may also be the case that Git finds that two of the commits are already there, but I *think* that should give you a conflict that requires manually `--skip`-ing those two commits. (Not sure about this last; the usual rebase process eliminates such commits when it can so that they never even come up in the first place.) – torek Feb 07 '17 at 01:24
  • I am using gitk. Yes, they are 4 successive commits. – Ondra Žižka Feb 07 '17 at 13:26
  • I'm pretty puzzled then. You can try individually cherry-picking each commit (and to automate it, use `git rev-list` to get the IDs from the range, which is what `git rebase -i` does, more or less) but that's precisely what the built-in sequencer code does for you, ever since Git 1.7.something. – torek Feb 07 '17 at 22:28
2

My first idea would be to do a git rebase. There is this rebase with three arguments in the man git-rebase page has this nice example (among many others):

                               H---I---J topicB
                              /
                     E---F---G  topicA
                    /
       A---B---C---D  master

$ git rebase --onto master topicA topicB

                    H'--I'--J'  topicB
                   /
                   | E---F---G  topicA
                   |/
       A---B---C---D  master

A similar effect would be achieved in your repository with the following commands.

First we create a branch with the last commit we are interested in, and checkout it:

$ git checkout -b newC myCommitC

Then we rebase this branch onto the desired place. Do not forget to specify the parent of the first commit, because you have to tell the last commit not included in the rebase;

$ git rebase --onto terraIncognita myCommitA^ newC

And done! Now you have newC pointing to the tip of the new branch:

terraIncognita <- myCommitA' <- myCommitB' <- myCommitC'
rodrigo
  • 94,151
  • 12
  • 143
  • 190