Upstreams et forks git : Guide pratique et astuce sympa

Nicola Paolucci
Nicola Paolucci
Retour à la liste

There are tons and then some useful guides on how to keep your forks updated against the upstream repositories. In this blog I will introduce you to few aspects of how forking interacts with upstream: the basics, the gotcha's, and an cool tip. To top it off I will then make you very jealous, or very eager, the choice is yours. Interested? Read on.

Le workflow de base pour actualiser le contenu et contribuer

Let me start by detailing a common setup and the most basic workflow to interact with upstream repositories.

In a standard setup you generally have an origin and an upstream remote - the latter being the gatekeeper of the project or the source of truth to which you wish to contribute to.

First, verify that you have already setup a remote for the upstream repository - and hopefully an origin too:

git remote -v
origin (fetch)
origin (push)

If you don't have an upstream you can add it simply with the remote command:

git remote add upstream

Vérifiez que la branche distante est correctement ajoutée :

git remote -v
origin (fetch)
origin (push)
upstream (fetch)
upstream (push)

Now you can collect the latest changes of the upstream repository with fetch (repeat this every time you want to get updates):

git fetch upstream

(Si le projet dispose de tags qui n'ont pas été mergés à la branche master, vous devez également exécuter : git fetch upsteam --tags)

Generally you want to keep your local master branch as a close mirror of the upstream master and execute any work in feature branches (that might become pull requests later).

At this point it does not matter if you use merge or rebase, the result will typically be the same. Let's use merge:

git checkout master
git merge upstream/master

When you want to share some work with the upstream maintainers you branch off master, create a feature branch and when you're satisfied you push it to your remote repository.

You can also use rebase instead then merge to make sure the upstream has a clean set of commits (ideally one) to evaluate:

git checkout -b feature-x
#some work and some commits happen
#some time passes
git fetch upstream
git rebase upstream/master

If you need to squash a few commits into one you can use the awesome rebase interactive at this point.

After the above, publish your work in your remote fork with a simple push:

git push origin feature-x

A slight problem rises if you have to update your remote branch feature-x after you published it, because of some feedback from the upstream maintainers. You have a few options:

  • Create a new branch altogether with the updates from you and the upstream.
  • merge the updates from upstream in your local branch which will record a merge commit. This will clutter the upstream repository.
  • Rebase your local branch on top of the updates from upstream and do a force push onto your remote branch:
git push -f origin feature-x

Personally I prefer to keep the history as clean as possible and go for option three, but different teams have different work flows. OBVIOUS ALERT!! You should do this only when working with your own fork. Rewriting history of shared repositories and branches is something you should NEVER do.

Astuce du jour : nombres de commits avant/après une branche dans l'invite

After a fetch, git status shows you how many commits you are ahead or behind of the remote the branch it syncs to. Wouldn't it be nice if you could see this information at your faithful command prompt? I thought so too so I started tapping with my bash chopsticks and cooked it up.

Une fois la configuration effectuée, voici à quoi ressemblera votre invite :


And this is what you need to add to your .bashrc or equivalent, just a single function:

function ahead_behind {
curr_branch=$(git rev-parse --abbrev-ref HEAD);
curr_remote=$(git config branch.$curr_branch.remote);
curr_merge_branch=$(git config branch.$curr_branch.merge | cut -d / -f 3);
git rev-list --left-right --count $curr_branch...$curr_remote/$curr_merge_branch | tr -s '\t' '|';

You can enrich your bash prompt with this new function ahead_behind to have the desired effect. I leave the colorization as an exercise for the reader (not to clutter the tip too much).

Exemple d'invite :

export PS1="\h:\w[\$(ahead_behind)]$"

Fonctionnement interne

Pour ceux qui souhaitent plus d'informations et d'explications, voici comment cela fonctionne :

Nous obtenons le nom symbolique du HEAD courant, c'est-à-dire, de la branche courante :

curr_branch=$(git rev-parse --abbrev-ref HEAD);

Nous obtenons la branche distante vers laquelle la branche courante pointe :

curr_remote=$(git config branch.$curr_branch.remote);

Nous obtenons la branche sur laquelle ce remote doit être mergé (avec un petit truc Unix pour tout ignorer, même la dernière barre oblique /) :

curr_merge_branch=$(git config branch.$curr_branch.merge | cut -d / -f 3);

À présent, nous avons ce dont nous avons besoin pour compter le nombre de commits qui se trouvent avant ou après la branche.

git rev-list --left-right --count $curr_branch...$curr_remote/$curr_merge_branch | tr -s '\t' '|';

We use the age old Unix tr to convert the TAB to a separator |.


I hope this basic walk-through on upstream is useful for those unfamiliar with the process. Also note that Bitbucket Server includes fork synchronization which basically relieves the developer from all the burden of keeping up to date with its forks, and Bitbucket Cloud has an easy 1 step sync, check it out!

Follow me @durdn and the awesome @Bitbucket team for more DVCS rocking.

Prêt à découvrir Git ?

Essayez ce tutoriel interactif.

Démarrez maintenant