13

I have a simple repository with linear history of commits, like:

[A] -> [B] -> [C] -> [D] -> [E] ...

I basically need to remove commits A and B so I thought I'd create a new repository and would like to achieve something like:

[X] -> [C] -> [D] -> [E] ...

So I created a new repository, manually created commit X that takes stores the relevant information from A and B and now need a command that will bring commits C, D, E etc. from the original repository and will put it on top of my new commit X.

How to do that?

Edit: Two problems I have with the suggested cherry-pick method are:

  1. Transferred commits lost their dates. Is there any way to preserve commit dates?
  2. When I fetched master from the original repository (and that remote master doesn't have any commit in common with the new repository), I have trouble deleting those fetched commits. When I do git branch -D myoriginalrepo/master, it says that no such branch exists while I can clearly see those commit in my GUI tool.
Borek Bernard
  • 50,745
  • 59
  • 165
  • 240

3 Answers3

18

Not sure why you need the commit date to stay the same but here goes:

git rebase B E --onto X --committer-date-is-author-date

If B..E aren't in the same repository as X (as they can be if you create your fresh start in-place), you'll need to fetch them first:

git fetch <path_to_old_repos>

Of course, B, E and X here mean their commit-ids if you haven't actually tagged/branched them.

You can also do something similar (although commit date won't be preserved) by rebasing from A in your original repository and squashing B onto it:

git rebase -i `A`

# change "pick b" to "squash b"

You'll get a chance to change commit message at which point you could make it X's.

mernst
  • 7,437
  • 30
  • 45
antak
  • 19,481
  • 9
  • 72
  • 80
  • This sounds like the best approach so far, however, I have a problem working with commits fetched using `git fetch`. How do I reference them? They are not on any branch, are they? – Borek Bernard Feb 01 '12 at 12:56
  • 2
    `git fetch` will create for you a convenience branch called `FETCH_HEAD` which will point to whatever `HEAD` was (i.e. whatever was checked out) in your old repository. Moreover, commit-ids will be exactly the same across repositories so you can just go `git log ` for instance. You can also choose to create a corresponding branch during fetch: `git fetch their_branch:my_branch`. – antak Feb 02 '12 at 01:00
3

You can use git cherry-pick. In your case:

git cherry-pick C D E

while X is your head should do it.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • Thanks, I thought that rebase will be my friend but cherry pick is the command to use. I hope I can specify commits like "C.." as I have hundreds of them in the original repo. – Borek Bernard Feb 01 '12 at 02:32
  • If you have lots, there are some cherry-pick options that will let you group them up. Check the documentation. Rebase will work too - if you use it on the original repository to replace A & B with X, you'll end up with the same result you're looking for. – Carl Norum Feb 01 '12 at 02:36
  • 1
    Cherry pick seems to lose the commit date information (it probably creates a new commit), will the problem be the same with rebase? That would be quite bad, I need to maintain the commit dates. – Borek Bernard Feb 01 '12 at 02:42
  • A rebase is preferred to cherry-picking a sequence – CervEd Feb 08 '23 at 07:43
1

You say you created "a new repository". You probably didn't want to do that. If for some reason the commits A and B must completely cease to exist (e.g. for legal reasons or because you accidentally committed your credit card number and your secret recipe for haggis) in your repository you will need to read about how to permanently delete a commit. But first let's fix the rest of the tree.

Try something like

git checkout -b freshstart A
# modify the state of the system until it reflects your desired new beginning X
git commit --amend
git cherry-pick C D E

Now you have a branch named freshstart which has your new X beginning and commits C, D and E assuming they merge nicely onto X.

tialaramex
  • 3,761
  • 1
  • 21
  • 23
  • That's right, A and B must be completely removed from the history so creating a new repo seems like a good way to start. I created a state X, fetched remote branch original-repo/master and now I can cherry pick commits from it into the main branch. Two problems I have: 1) Cherry picked commits lose their date 2) I cannot reference the original-repo/master branch (as it doesn't have any common parent with local master and stands beside my 'master' I guess). I cannot delete that temporary branch I cherry-picked from. – Borek Bernard Feb 01 '12 at 02:55
  • 1
    You don't need a new repo to completely remove commits. You can use rebase, for example, to eliminate commits. The only thing is that if you have legal reasons or something for removing commits you have make sure that the objects that become garbage when you remove the commits get cleaned up so they aren't still available to people that know what they're doing. And of course you have to go to every clone of the repo everywhere and do the same thing or you may not be living up to the legal obligation. – bames53 Feb 01 '12 at 05:17
  • A and X don't have any commit previous in common, is it possible to create a branch that doesn't have a parent in my original repo? – Borek Bernard Feb 01 '12 at 10:28