dragster
dragster

Reputation: 448

Commit changes from local repository to existing repository hosted on BitBucket

I am new with git so please bear with me. I have a local repository with a lot of code which uptil now I was sharing with my friend using mail. Today my friend has setup a git repository on bitbucket with some changes.

I have also made some changes to my local repository. Now I want to commit my changes to the remote repository but I fear I'll mess up the files. I have done this uptil now -

git init
git remote add origin <remote-url>

I don't know what to do next. Please suggest.

Upvotes: 1

Views: 595

Answers (2)

torek
torek

Reputation: 488103

Since you already have an existing repository in which you have been working for some time, you already have some commits that create a history that is not related to the history in your friend's repository.

If you do not already have a good idea about what Git commits are and what they do for you, and why I draw my commit graph fragments the way I do, see, e.g., this answer to an unrelated question. Pay particular attention to the term root commit, and note how commits link up. Note also that each commit gets its own unique hash ID (which I abbreviate as single-letter names). These hash IDs matter a lot.

Your existing repository has some commits. For convenience, I'll just assume you have just four, all found by the name master, which I will call A through D:

A--B--C--D   <-- master

This is your repository, handled by your Git. In this repository, you have now run:

git remote add origin <remote-url>

with the remote URL being something that tells your Git to call up Bitbucket over the Internet-phone and get their Git to talk to yours, with their Git looking at your friend's repository. So origin is now short for "the URL by which I get my Git to talk to their Git to get commits from, or give commits to their Git." If your friend is named Jim we can call this "Jim's repo". I can try to use origin everywhere but I find this is less effective, so I'm going to run with Jim for a bit. :-)

Obtaining Jim's work

Your next step is to run:

git fetch origin

which instructs your Git to call up their Git and get commits from them. I don't know how many commits there are in Jim's repo origin, but for convenience I will just draw three. They are guaranteed to have different IDs from your four commits, because Jim made them, not you—they have Jim as their author and committer. Moreover, Jim made them on Jim's schedule (so they his timestamps, not yours), using Jim's previous commits (so they have his parent IDs, not yours), even if they have the same saved snapshots. So we need to give them different letter names.

Your Git will ask their Git for their commits. More precisely, it will ask for the ones they have that you don't. Initially, that's all of theirs. So your Git gets all their commits. These all have different IDs from all of your commits. So let's draw them in:

A--B--C--D   <-- master

P--Q--R      <-- origin/master

There are two things you should note right away, in either order:

  • Where did the name origin/master come from?

  • Why are there two root commits?

The answer to the first is that your Git renames their Git's branches. It does this because it has to: your Git needs a new, independent name for their branches, so that their names can be maintained separately from yours.

The standard renaming is to take their branch name and shove the name of the "remote" in front. You gave the remote the name origin, so Jim's master is now your origin/master.

There are two root commits because Jim made his first commit by himself, without using any of your commits. (How could he have used yours? He never had them!) His commits' actual IDs are big ugly hashes, rather than these one-letter names. This is why I jumped from D to P. That leaves a lot of in-between room, and avoids implying that, say, E comes right after D.

Commits P and A are entirely unrelated, and this is our big stumbling block. You need to decide, and now, what to do about this, and there's no right answer.

Merging

You can try to merge these two unrelated histories. Older versions of Git (earlier than 2.9) will attempt it: all you have to do is run git merge origin/master. Newer Git versions realize that this is unlikely to work well, and force you to add the flag --allow-unrelated-histories. Adding the flag does not make it work any better, it just tells Git to go ahead and attempt it anyway. If you choose this method, you will have to resolve all the merge conflicts, and then make a final commit with that merged result:

A--B--C--D--E   <-- master
           /
P--Q------R     <-- origin/master

This merge commit ties the histories together. If Jim allows it, you can now git push this new commit E, plus your A-B-C-D that go with it, to the Bitbucket repository. You would have your Git send all those commits, plus a request that their Git set their master to point to commit E (or rather, its actual, full, incomprehensible hash ID).

Rebasing

Alternatively, you can try to rebase—i.e., copy—your commits, or rather, some subset of your commits, onto their commits. For instance, if your commit A is "sufficiently close" to their commit P, in terms of its saved source tree, then you might want to just capture what you changed in going from A to B, then what you changed in going from B to C, then what you changed in going from C to D. If you pile each of these, sequentially, atop their commit R, and get your Git to remember your copy-of-D as your master, you will get this:

A--B--C--D        [abandoned]

P--Q--R           <-- origin/master
       \
        B'-C'-D'  <-- master

where B' is the copy of B, C' is the copy of C, and D' is the copy of D.

There are more options

There are lots of ways to combine all these things. The key to giving these back to Jim, or otherwise sharing all this work, though, is getting the various commits to become related. You need to have some commit(s) in common—some point where, when we draw these commit graphs, the arrows that connect commits to their parents "join up" at a commit. Once you have that, the rest is much easier.

Jim still needs to give you write access to his repository, or—as is commonly done on GitHub and Bitbucket—you can create yet another repository-copy on the server (on Bitbucket or on GitHub) that Jim can access. You then push your commits to your repository on that server, and send Jim some kind of alert—email, for instance—telling him "I have new commits in my widely-accessible repository, that you can fetch and merge into your widely-accessible repository. Please pull them at your convenience." We generally call these pull requests, and if you use some of the "fork a repository" buttons on some of these web sites, they automate a big chunk of what you need to do to deliver these alerts to the other person or group (the one I have been calling "Jim" here).

In all cases, though, your first step is to do something so that your commits become related, by parent/child linkage, to their commits. What, exactly, you want to do here is up to you. Note that if you are not sure what to do, you can always clone your current clone, mess around with it all you like, and see if you like the result. If not, you can remove that clone and re-clone the current clone again and mess with the new one. That gives you an easy way to test out each path.

(There are ways to do this without cloning your clone—Git is very good about not losing commits—but they can be tricky because if you hate the result and want to lose it, well, Git is very good about not losing commits, which makes it very bad about losing them. Working in a separate clone-copy makes all such worries go right out the window.)

Upvotes: 1

eftshift0
eftshift0

Reputation: 30204

I guess you should fetch... but then you can't just "checkout" because you already have files of your own and git will probably warn you about it if you try to checkout just like that. The best thing you should do is

  • Create a patch of the changes you have
  • Optionally: create a local branch from the branch set up on the remote repo (master?)
  • Checkout (--force) remote (or local branch, if you created it)
  • Apply your patch and commit
  • Optionally push
  • Continue having fun with git

Upvotes: 1

Related Questions