Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I re-play my commits of a local Git repository, on top of a project I forked on github.com?

Tags:

git

github

Yes, I know I should have just forked the project from the beginning, but this is what situation I'm in now. :)

I have a local Git repository that contains my blog, on my local computer, that has several months of commit history. Originally, I simply downloaded the files from the repository http://github.com/mojombo/mojombo.github.com, and I continued on with my local Git repository, with the first commit looking like the latest files from mojombo's repository.

I would now like to fork the project and have my local Git repository commits be replayed on top of it, so it looks like I forked the project from the beginning, and then pushed it back to my forked version of mojombo's repository, on my GitHub account:

http://github.com/program247365/mojombo.github.com

So perhaps the history would then look like this:

mobjombo repository:         1---2---3----23
                                  \
my blog repository commits:       24---25---

What Git commands can I use exactly to do this?

I've looked at this question. Will I have to add mojombo's repository as a remote to my project, and then pull it in, merge, resolve conflicts, and then push to my forked project on GitHub?

like image 664
program247365 Avatar asked Sep 21 '09 22:09

program247365


People also ask

How do I sync my original forked repo?

To sync your forked repo with the parent or central repo on GitHub you: Create a pull request on GitHub.com to update your fork of the repository from the original repository, and. Run the git pull command in the terminal to update your local clone.

How do I reset local commits?

If your excess commits are only visible to you, you can just do git reset --hard origin/<branch_name> to move back to where the origin is. This will reset the state of the repository to the previous commit, and it will discard all local changes.


1 Answers

In short:

One solution is to use grafts to connect history, then use git filter-branch to rewrite history according to those grafts, then optionally do a merge.

Note that solution to replay your changes (your commits) on top of new development in original repository (the rebase solution) is another viable solution.


Longer version:

Let's assume that you either remember, or you can find by examining source and/or using git commands the revision of repository you downloaded snapshot of, and started local development. Let's call this revision START or A.

Let's assume that you local disconnected history is in the clone of original repository. It means that your local disconnected development is in the same repository as full history of a project. Let's assume that you local branches are in branch 'master' (and for simplicity that there is only one branch).

If you didn't fetched project into repository with your local disconnected work, you can do this with:

$ git remote add origin git://github.com/mojombo/mojombo.github.com.git
$ git fetch origin

The history looks now like the following:

*---*---*---*---*---A---*---*---*---*      <--- origin/master (remote-tracking branch)

                                     x---y---*---*---*      <--- master (your local disconnected history)

The commit named A in above diagram is the START commit you downloaded as snapshot and started your local development off.

There are two possibilities: you have comitted snapshot of 'A' as an initial commit 'x', or the first commit you made was with your local modifications.

In first case (you committed original starting state, e.g. as 'Initial commit' or 'Import') you would want the connected history to looks like this:

*---*---*---*---*---A---*---*---*---*      <--- origin/master (remote-tracking branch)
                                      \
                                        \-y---*---*---*       <--- master (your local disconnected history)

i.e. your first original commit 'y' to have 'A' as a parent.

In the second case (you committed with your changes) you would want the connected history to look like this instead:

*---*---*---*---*---A---*---*---*---*           <--- origin/master (remote-tracking branch)
                                      \
                                        \-x---y---*---*---*      <--- master (your local disconnected history)

i.e. you want first commit 'x' to have 'A" as a parent.

In both cases you want to find full SHA-1 identifier of commit 'A', and full SHA-1 identifiers of commits 'x' and 'y'.

You can find SHA-1 of commit 'A' (assuming that you don't know it already) with git rev-parse:

$ git rev-parse A     # or A^{commit}
437b1b20df4b356c9342dac8d38849f24ef44f27

(the '^{commit}' suffix might be needed to make sure that you found commit SHA-1, which is important if you for example know 'A" by its tag, e.g. 'v0.99'; in your case it is not necessary, as the repository in question doesn't use tags).

You can find SHA-1 of commits 'x' and 'y' using git rev-list (assuming that you development was done on branch 'master'):

$ git rev-list --topo-order master | tail -2
8bc9a0c769ac1df7820f2dbf8f7b7d64835e3c68
e83c5163316f89bfbde7d9ab23ca2e25604af290

(the "| tail -2" is here to find last two commits in generated list; you don't need to use it if you don't have it).

Note: in all examples above full SHA-1 are examples, and should not be used as is!

Let's name the commit that you want to have 'A" (or 'START') as a parent as FIRST (it would be 'x' or 'y', depending on you case, as stated above). Now we use grafts mechanism to connect history:

$ echo "<SHA-1 of FIRST> <SHA-1 of START>" > .git/info/grafts

Then you should check if you now have correctly connected (joined) history, by using graphical history browser such as gitk, or QGit, or GitX is you are on MacOS X, or even "git log --graph", or "git show-branch", e.g.:

$ gitk master origin/master    # or --all

(where gitk is here only as an example; if you use "git show branch" you not always can use '--all' option).

Finally we would probably want to make those changes permanent, so anybody who would fetch from our repository would also have connected history. We can do this by using git filter-branch:

$ git filter-branch master

You would have original (disconnected) history in 'refs/original/master'.

Now you can remove grafts file:

$ rm .git/info/grafts

Now you would be able to merge in new development for original repository:

$ git merge origin/master

Setting up per-branch configuration, so that it would be enough to do simply "git pull" when on branch 'master' to pull (merge) changes in origin(al) repository is left as exercise for the reader... :-)


Note: the rebase solution would result in the following history (assuming that we have case where first comit was simple import):

*---*---*---*---*---A---*---*---*---*                                      <--- origin/master (remote-tracking branch)
                                                                     \
                                                                       \-y'---*'---*'---*'      <--- master (your local disconnected history)

(where y' means that commit y was modified: it should be about the same changeset, but it is different as a commit).

like image 185
Jakub Narębski Avatar answered Oct 22 '22 07:10

Jakub Narębski