Sync all branches with git
Asked Answered
P

5

32

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?

Prism answered 26/11, 2014 at 19:16 Comment(8)
You don't want to do this. It's a common misconception. When you "fetch" you will get all the updates, then you can manually update any local branch you have whenever you need to use it.Peaked
@AndrewC but that's what I don't want to do, ie: update manually each local branch one at a time. I want my repo to be completely synced with a single command. Is this not allowed?Prism
When you sync your repo is completely synced. If you use git branch -r you can see all the references exactly as they are on the remote. git branch is showing you the stuff that is local to that repo only.Peaked
When I sync? When am I syncing? That's precisely the question.Prism
git fetch. This will update your remote references, and additionally bring in all the needed objects (commits, trees, blobs, etc). Always git fetch to bring a repository up to date. What you do with your local work at that point is best dealt with on a branch by branch basis.Peaked
Fetching doesn't really resolve much other than allowing me to see what's changed. I don't want to deal with syncing branches one by one, that's the whole point of the question.Prism
I get that. Git only updates local branches if they are checked out to your working directory (if we ignore forced updates and whatnot). Whether or not a local branch is up to date if it isn' currently checked out doesn't really fit in the normal git model.Peaked
Also see: #4318661Laudation
S
22

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.

Servitor answered 27/11, 2014 at 10:13 Comment(3)
Thanks @Breen, couple of questions: What is the difference between git fetch origin and simply git fetch? Will git reset --hard origin/master sync only the master branch or all the other ones too?Prism
While git fetch fetches all branches, as the question asked, the other two commands only affect the current branch.Metallurgy
For git fetch, the default repository is origin unless the current branch has a different upstream branch set (which would be unusual).Metallurgy
E
41

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
Elgar answered 14/4, 2021 at 17:5 Comment(3)
at best add this to your bashrc file \\ alias gitcleanup='git fetch --all -Pp'Bruin
git fetch will update git's local copy of the remote repo but it won't change what you see in any local branch. For that you would do git pull (which does git fetch first) in each branch. That may report conflicts between local and remote changes to that branch. You will then need to resolve those conflicts.Metallurgy
Indeed, if you have conflict you need to solve it , or just delete the local branch and checkout it again to get a fresh updated clone of the remote branch.Elgar
S
22

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.

Servitor answered 27/11, 2014 at 10:13 Comment(3)
Thanks @Breen, couple of questions: What is the difference between git fetch origin and simply git fetch? Will git reset --hard origin/master sync only the master branch or all the other ones too?Prism
While git fetch fetches all branches, as the question asked, the other two commands only affect the current branch.Metallurgy
For git fetch, the default repository is origin unless the current branch has a different upstream branch set (which would be unusual).Metallurgy
H
11

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.

Harmonic answered 6/7, 2018 at 10:25 Comment(1)
It has been an hour git was so stupidly refusing to update my local branch with the remote one, and that I have been crawling desperately through all answers of SO to resolve this. I just copy pasted your answer without even trying to understand, and it totally solve my problem with your sync alias. Great answer!Indoeuropean
O
3

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.

Ocko answered 26/11, 2014 at 19:50 Comment(2)
I'm sorry but I think you completely missed the point of the question. I never said I could see changes before pulling. I said that what I saw before pulling was the old or previous version of my repo. I'm after a way to simply sync all my branches at once, that's it.Prism
@Prism I've edited answer, is this what you are looking for?Ocko
P
0

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://mcmap.net/q/381191/-git-automatically-fast-forward-all-tracking-branches-on-pull

Peace answered 1/4, 2021 at 5:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.