views:

1120

answers:

6

Hi all,

I have a local branch for day-to-day dev work in git. My workflow is:

  1. Do stuff on local_branch, commit
  2. Fetch origin/master
  3. Rebase local_branch to catch up with new stuff from origin/master

It all works fine, however most of the recommendations I encountered say that one should not "push" private branches, on which rebase is regularly performed.

The problem here is that in this case local branch is not backed up to a server and the only way to save the work is to merge it back to "pushable" branch (i.e. origin/master)

What would be your recommendations on the workflow in this case?

The preference is to avoid usage of external backup utilities and use just git.

Thanks!

UPD: I have actually found that one of the former requirements put in my question - avoid usage of external utilities - is actually unnecessary limiting. I found that when I use DropBox with my repositories it's actually quite nice and requires no effort to backup stuff - it's all done automatically.

+1  A: 

Can you set up another remote repository that you do push all of your branches to? The other thing to consider is just backing up everything (important) on your local machine, including the git repo.

Cebjyre
In case of setting up separate repository I'll still have a problem of changed history - i.e. I won't be able to push straight after rebase
Art
A: 

There is nothing wrong with pushing to the same branch that you are rebasing from. These diagrams should illustrate why this works fine:

Lets say this is what the commit graph looks like after you've branched local_branch and made a couple of commits to it (C and D). Someone else has made one commit (E) to origin/master since you branched local_branch:

A -- B -- E  [origin/master]
      \
       \    
        \-- C -- D  [local_branch]

Then after running "git rebase origin/master", the commit graph will look like the next diagram. "origin/master" is still the same, but "local_branch" has been rebased:

A -- B -- E  [origin/master]
           \
            \
             \-- C -- D  [local_branch]

At this stage, if you do "git push origin local_branch:master", then it will result in a simple fast-forward. "origin/master" and "local_branch" will be identical:

A -- B -- E -- C -- D  [origin/master],[local_branch]

Now you are free to do more work on "local_branch". Eventually you might get something like this:

A -- B -- E -- C -- D -- G -- I [origin/master]
                     \
                      \
                       \-- F -- H [local_branch]

Notice this looks a lot like the starting graph. You can keep repeating this process over and over.

You should avoid pushing to some other branch, one that you are not rebasing from. That is where you will run into trouble (to the other branch, it will look like your "local_branch's" history has suddenly been re-written, after you've rebased from "origin/master").

Dan Moulding
This is not quite what I want. Suppose the work is in progress and I don't want to merge it into master and just want to backup it.
Art
Oh, I totally misunderstood your question. Sorry about that. It seems to me that a reasonable solution would be to clone your local repository to some location that *is* backed up. Just periodically re-clone to keep your backup up-to-date. But you don't necessarily need to do any work in the clone (you can just delete it before you re-clone to make a new backup).
Dan Moulding
@Dan, is this the workflow you would get when doing "git pull --rebase"?
Casey
@Casey: Yes. Normally 'git pull' is equivalent to 'git fetch' followed by a 'git merge'. But 'git pull --rebase' is equivalent to 'git fetch' followed by 'git rebase', and it results in the kind of workflow described above.
Dan Moulding
+1  A: 

Here's what I do. But - this isn't private. I do this so that I can collaborate with myself (so to speak). It lets me work on the same branch on two or more boxes. if other people had access to the shared repo, they could see the work I'm doing on the branch. Of course, on my home repos, nobody else has access, so it's still private. On github, all the world could see my stuff. Like they really care. ;)

Don Branson
You don't seem to be doing any rebases, which is the actual problem I have.
Art
Wouldn't it work to "git pull" your local copy then rebase locally? Then your local branch would have all the changes from master merged in, and the next push would push to the remote branch. I would have to try this to know for sure, but it seems like that would work.
Don Branson
Don, next pull after rebase on non private branch causes a conflict.
Art
Hmm. What's the conflict? Was it the classic case of separate changes made to the same code block on separate branches?
Don Branson
+2  A: 

Another option would be to push "local_branch" to the "origin" repo, but to it's own branch in that repo (not "master"), i.e.:

git push origin local_branch:local_backup

Then when you are ready to make another backup (and after you've been doing some work and rebasing) just delete the backup branch from the origin repo before you push it out again:

git push origin :local_backup <=== deletes the branch from origin

git push origin local_branch:local_backup

This way you won't run into problems pushing "local_branch" after it has been rebased from "origin/master".

And if deleting your backup branches makes you nervous (until after you've finally committed your work to "master"), you could always keep pushing to a new branch with a new name (e.g. "local_backup1", "local_backup2", etc).

Dan Moulding
would deletion be an unnecessary step if one uses push --force?
Art
Yes, --force is a good alternative to deletion. It will replace the remote branch head with the new one from your local branch.
Dan Moulding
By default, remote repositories will reject non-fast forwards so --force may not be enough. http://stackoverflow.com/questions/253055/how-do-i-push-amended-commit-to-the-remote-git-repo/255080#255080
Charles Bailey
@Charles: That's exactly what the --force flag is for: bypassing git's default refusal to accept a non-fast-forwarding push.
Dan Moulding
No. The force flag is not - and should not be - for overriding the remote repository's behaviour of rejecting non-fastforward push attempts when receive.denyNonFastForwards is set. In these cases the only user-observable difference that --force will give is changing the error message from '[rejected]' to '[remote rejected]'.
Charles Bailey
Right, but it *is* for forcing fast-forwards when receive.denyNonFastForwards is *not* set. It is not set by default, except for "shared" repositories (which are also not created by default). Notice that I said "git's default refusal". If you add "receive.denyNonFastForwards" to your configuration, you are telling git to not allow non-fast-forwards even when forced.
Dan Moulding
@Charles: Just thought I'd add, for clarity, in case you didn't get this already, that git will deny non-fast-forward pushes even without the "receive.denyNonFastForwards" configuration variable set (unless forced, of course).
Dan Moulding
I get it, I just wanted to make sure that we documented that '--force' isn't sufficient in all cases. Most central repositories are be created as 'shared' (it's the simplest way to set up a multi-user enabled shared repository), hence my original comment that *remote repositories* will reject non-fast forwards which you appeared to contradict. I suppose I could have expanded on 'by default' a bit more, but I thought that my linked answer had a clear enough explanation.
Charles Bailey
A: 

There's nothing wrong with pushing personal branches. It is generally discouraged because people might start work based on your branch, and when you rebase, their changes are left floating.

What I do is use a prefix to denote "this is my branch, use it on your own risk", like: fc-general-cleanup.

felipec
+11  A: 

I use the --mirror option and push to a personal backup repository:

Add it as a remote:

git remote add bak server:/path/to/backup/repo

Do the backup:

git push --mirror bak

This will automatically make your backup repository look like your active one -- branches will be created, deleted, updated (even forced/non-fastforwards) as needed. You can make an alias for this too:

git config alias.bak "git push --mirror bak"

Then, it's just a matter of running "git bak" when you want to do a backup. You could also throw this into a cron job.

Pat Notz
Thanks for --mirror and config alias, will look into it
Art
Just a heads up, this will *not* mirror your configuration files from your `.git` directory (`config`, `hooks/*`, etc.). Just a heads up. Still, I think it's a good solution.
Pat Notz