简体   繁体   中英

Rewriting git history with a private remote repository

I use a private remote repository for two main reasons, backup my work in progress when I travel from the office to home or synchronise code between two development machines.

I have recently started playing with git (coming from hg) and I like the idea of rewriting history. What I found recently is if I do the following

  • # do work
  • git commit
  • git push
  • # Fix a little problem with the last commit
  • git commit --amend
  • git push

There is a conflict with the remote. I need to pull, merge and push again.

  1. Is there a way to mirror my local changes to the remote repository?
  2. On my second machine, how do I pull those mirror the remote into my local (preferably without a full clone again)

I know that I'm the only one using it, so I know that no one else will have to deal with a changing history.

You can force a push with git push -f remote-name branch-name then just git pull on the second machine. If there is a conflict you can force the pull with git fetch --all followed by git reset --hard remote-name/branch-name . You'd better be sure what you're doing though, rewriting history as part of your regular workflow isn't recommended.

I meant to say forcing pushes and pulls regularly isn't recommended.

You can also execute a

   git reset HEAD^ --hard 

on the remote repository and redo git push after performing a git pull form the local repository. I think it is somewhat less dangerous, though more involved, than the git push --force because you only need to validate that you are rewinding the exact right thing and you can always keep a safe copy on the remote repository with

    git checkout -b safe-copy-old-branch-name

before doing everything which not only keeps an original of the previous "intentions" behind the commits but also keeps the exact same (orignal) SHA1.

You can literally set up the "exchange / backup" remote as a mirror (a bare clone that takes "force" style ref updates, rather than requiring that some or all ref updates be fast-forwards).

"Normal" clones are set up to do force-updates of refs/remotes/* : take a look at .git/config (or use git config --get remote.origin.fetch ) and you'll see that the fetch refspec for origin is:

+refs/heads/*:refs/remotes/origin/*

The plus sign means "force", ie, "don't require a fast forward". Hence, when you git fetch origin , whatever commit SHA1 that (eg) origin's refs/heads/master names, becomes your local refs/remotes/origin/master , no matter what commit SHA1 your refs/remotes/origin/master used to point-to. This is generally safe (and hence the default even for "ordinary" clones) because these "remote branch" SHA1-pointer names wind up in refs/remotes/ rather than refs/heads , ie, it does not affect your local repo's branch name-space in any way.

When you use git push -f —or specify a push refspec starting with a + , which means the same for push as for fetch—this tells the remote that it should also allow non-fast-forwards. (That is, it tells the—for want of a better phrase—"built in hook" to allow fast forwards. Other git hooks on the remote to which you are pushing can still reject the change.) This is generally not safe because it affects the remote repo's name-space: you're updating its refs/heads/master , not something like refs/pushes/JohnsMachineAtHome/master . But if you know what you're doing, and don't make mistakes (or at least not often :-) ), it's OK after all.

Remember also that every commit in git is reasonably sticky as long as there is some reference (eg, the reflog) pointing to it, so in that particular sense it's safe to force-push and force-fetch as long as you commit first. (As in, if you goof up, you might have to ssh in to work/home from home/work and poke around in the reflog to find a commit you dropped on your mirror.) Be aware that bare clones (including bare mirror clones) don't keep all that much in the way of reflogs (there is some data kept, especially with core.logAllRefUpdates set; see git config docs) since the updates they get are en-masse at push or fetch time.

See also these git config options:

  remote.<name>.push The default set of "refspec" for git-push(1). See git-push(1). remote.<name>.mirror If true, pushing to this remote will automatically behave as if the --mirror option was given on the command line. 

you can work on a temporary branch and rebase it before merging it with the master branch. Rebasing (--> 'git rebase -i master') allows you to reorder and edit commits.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM