'Sync all branches with git

I push code from two main locations: my PC at home and my laptop at work. I use Github to store my repos.

This is the scenario: I did some work in my PC on a repository I've been working on for some time (both in my PC and in my laptop), and ended with the following branches:

$ git branch
* master
* v123
* test-b

which I pushed to Github. So far so good.

Now I'm on my laptop and this is what I see before attempting to pull anything:

$ git branch
* master
* v_123

This is an old version of my repo in my laptop (since I've been working in my PC) where the differences are: a branch is missing (test-b), another one has been re-named, or equivalently deleted and re-created with a new name (ie: v_123 is now v123), and lots of things have changed possibly in all branches.

I want to sync all my branches into my laptop and have them correctly tracked. I've looked at two of the most up-voted questions regarding branch cloning/fetching (How to clone all remote branches in Git?; How to fetch all git branches) and right now I'm a bit lost.

Is there some easy to use git sync-branch --all command that can be used to sync my laptop with the latest state of the repo in Github?



Solution 1:[1]

Not sure, this is what you expect.

git fetch origin
git reset --hard origin/master
git clean -f -d

The above commands will synchronize the remote repo with the local repo. After the above command execution, your local repo will be like the mirror image of your remote repo.

If you want to retain the changes as unstaged files, use --soft instead of --hard.

WARNING: All your untracked files will be gone when you do git clean -f -d.

Lemme know, if any questions.

Solution 2:[2]

I think you want to:

git fetch --all -Pp

where: git fetch Download objects and refs from another (remote) repository

--all fetch all remotes.

-P remove any remote-tracking references that no longer exist on the remote.

-p remove any local tags that no longer exist on the remote.

for more use git fetch --help

We have a similar command that only perform a prune not fetching remote data:

git remote prune origin

We can also set the git client to do the remote prune every time we fetch data:

git config --global fetch.prune true

Solution 3:[3]

I had a similar issue:

  • I want to sync all my local tracking branches at once
  • I want this operation to be safe. In other words, in case of local modifications or diverging branches, warn and don't touch the history (and do not tamper with uncommitted modifications)

After searching a lot for a simple solution, I ended up with my own (not so simple) solution, based on a series of git aliases (code to be added to the .gitconfig file): https://gist.github.com/arnauldvm/dcec7ee043c25dce30dbae1b576f2102

? This is fresh code, not yet so heavily tested.

Usage: git sync


Some explanations:

tracking = "!f() { git for-each-ref --format '%(refname:short):%(upstream:short)' 'refs/heads' | egrep -v ':$'; }; f"

? This will retrieve all local tracking branches and the corresponding remote branch, colon separated.

is-clean-workdir = "!f() { git diff --stat --exit-code || { echo \"Workdir dirty\"; exit 1; }; }; f"
is-clean-index = "!f() { git diff --stat --cached --exit-code || { echo \"Index dirty\"; exit 2; }; }; f"
is-clean = "!f() { git is-clean-workdir && git is-clean-index; }; f"

? These will verify that there are no pending changes in the workdir.

co-merge = "!f() { local=\"$1\"; remote=\"$2\"; git checkout \"$local\"; git merge --ff-only \"$remote\"; }; f"

? This takes 2 arguments (a local branch and a remote branch), it will checkout the local branch and merge it with the remote branch. It uses --ff-only to avoid creating commit merges ? In case of diverging local branch, it will issue a "fatal: Not possible to fast-forward, aborting." message on STDERR.

current-branch = rev-parse --abbrev-ref HEAD

? Retrieve name of the branch currently checked out. Will be used to put the workdir back in its initial state, at the end.

sync = "!f() { git is-clean || { echo Aborting sync.; exit 1; }; current=$(git current-branch); git fetch --all; git tracking | while IFS=: read local remote; do echo \"Merging $local with $remote\"; git co-merge \"$local\" \"$remote\"; done 3>&1 1>&2 2>&3 | egrep -i --color 'fatal|$' 3>&1 1>&2 2>&3; git checkout \"$current\"; }; f"

? That's the main part. Let's split this into parts:

git is-clean || { echo Aborting sync.; exit 1; }

?? Aborts the command if there is pending work in the workdir.

current=$(git current-branch)

?? Stores the name of the branch currently checked-out.

git fetch --all

?? Sync remote branches from all remotes (--all).

git tracking | while IFS=: read local remote; do ...

?? Iterates over each local tracking branch.

3>&1 1>&2 2>&3 | egrep -i --color 'fatal|$' 3>&1 1>&2 2>&3

?? Highlights errors (containing the 'fatal' keyword). The "3>&1 1>&2 2>&3" magical formula exchanges STDOUT and STDERR, it is necessary to be able to pipe STDERR to the egrep command.

git checkout "$current"

?? Restore the workdir to its initial state.

Solution 4:[4]

You cannot see any changes made on remote before attempting to pull anything. You must fetch from remote first.

There is a reason for this. Git is decentralized. Every repository contains it's full copy. It means that you may create a full clone from repository on your disk. And it also means that you can be working entirely offline, unless you command it to sync. Actually, the way git is made forces you to work almost entirely offline, not counting moments you push or fetch. On the other hand, you have full control and nothing happens without you explicitly saying so.

How to do it? That's why git pull exists. It performs two operations. First, it fetches all branches from remote (equivalent to git fetch). They are saved as <remote>/<branch> and not listed on branch list by default (override with -a). Then, it merges currently active branch with it's tracked branch.

Now, if you want to have a remote branch tracked, then simply checkout it after pull. It will create it from last synced version and should set everything up. If you want to set nonpublished local branch tracked on remote, use git push with -u flag. It will set upstream for the branch as push target.

EDIT:

So your goal is to update all local tracked branches at once, with one command? Not those <remote>/<branch>, but your local branches, without calling pull on every single one of them, now I get it right?

Unfortunately, it's impossible to do with git alone. But there is an extension that does exactly that. From project description

git-up - fetch and rebase all locally-tracked remote branches

You can find it here http://aanandprasad.com/git-up/

When you use it, with command git up, it will stash your current changes, fetch all remotes, then rebase all tracked branches to state from tracked remote and unstash changes. I believe this is the way of synchronization you want.

Solution 5:[5]

Here is a script I wrote that will fetch and fast forward all your branches without doing any checkouts (so it's faster and doesn't affect your current working tree.

https://stackoverflow.com/a/24451300/965176

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 not2qubit
Solution 2
Solution 3 Arnauld VM
Solution 4
Solution 5 qwertzguy