tags:

views:

282

answers:

3

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 this repo: http://github.com/mojombo/mojombo.github.com and continued on with my local git repo, with the first commit looking like the latest files from mojombo's repo.

I would now like to fork the project, and have my local git repo commits be replayed on top of it, so it looks like I forked the project from the beginning, and then push it back to my forked verson of mojombo's repo, on my github account:

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

So perhaps the history would then look like this:

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

I'd like to know what git commands, I can use exactly, to do this?

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

+4  A: 

Here's a thought on what you could do. It's kind of the opposite from the idea you summarized at the bottom of your question.

  1. Fork mojombo's repo in GitHub.
  2. Clone your forked copy.
  3. Merge in your changes from your existing (original) repository.
  4. Delete your original repository (if desired, but you don't really need it anymore).

So basically, after forking on GitHub, you might use the following sequence of commands:

$ git clone git://github.com/$YOUR_USERNAME/$YOUR_PROJECT.git  # Clone your GitHub fork (#2 from above)
$ git pull /path/to/your/original/repo master:master           # Clone your original repo's master branch into your new repo (cloned from GitHub)
$ rm -rf /path/to/your/original/repo                           # Might as well delete the original -- you don't need it anymore, since all your history is in your new repo

In summary, this method will merge in all the changes you've made in the older repo (thus preserving your development history), while also pulling in mojombo's history, and allowing you to keep up with mojombo's changes/easily contribute changes back to his repo, if applicable.

mipadi
+5  A: 

When I tried git pull it gave me the following error:

$ git pull grid master:master
! [rejected]        master     -> master  (non fast forward)

In my particular case, it seemed that git rebase was the way to go for me, as shown by the steps here:

#Clone my forked project from github
git clone [email protected]:program247365/mojombo.github.com.git 

#Add my repo as a remote repo, with the alias 'grid'
git remote add grid "path/to/remote/gitrep/with/all/history/unrelated/to/mojombo/" 

#Rebase my commits on top of mojombo's
git rebase master grid/master

#Switch to the local master branch 
git checkout master

#Call up my mergetool via git, to start rectifying the conflicts that emerge between my repo, and mojombo's
git mergetool

#Push my rebased/combined repo back to Github.com
git push github
program247365
That'll work. The reason it wouldn't `pull` is because of those conflicts; the other solution is to do a `git fetch` followed by a `git merge`.
mipadi
Note that `git pull` normally _does_ a `fetch` followed by a `merge` operation, but generates that error if the `merge` does not result in a "fast-forward commit".
mipadi
Assuming that your first commit was just 'import', you could got the same results as with rebase by exporting your commits to patches using `git format-patch`, doing a fresh clone, then importing your work from patches using `git am --3way`.
Jakub Narębski
@mipadi @Jakub Nerebski Thanks! I like hearing about alternate ways of doing things in Git. Upvotes for your comments.
program247365
+4  A: 

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 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).

Jakub Narębski
Awesome answer. Learned a lot. Thanks.
program247365