Chapter 19 Git basics

19.1 Introducing yourself

Git will first need to know who you are. You can specify this using Git Bash. Note that it’s important to use the email address that you also used for your Git repo manager account, because when Git talks to that that server, that email address is how you’ll be identified. To set your username and email address, use:

git config --global user.name "Gjalt-Jorn Peters"
git config --global user.email "gjalt-jorn@behaviorchange.eu"

Note that you should probably use your own name and email address, though.

The first time you’ll ask Git to do something that requires it to authenthicate with the Git server hosting the remote repository, Git will ask for your password. Usually, Git will store this in its credential manager so you’ll only have to provide it once.

19.2 Unsetting your password

Sometimes you may want/need to force Git to forget your password. Operating systems often come with thir own credential managers that applications can use to store passwords, but Git also has its own credential manager. Which one Git uses depends on how you configure Git (it lets you choose when you install it, but you can also change this later on).

A number of methods to reset your password are listed on this Stack Overflow page.

I think this one either always works, or only if you use the inbuilt Git credential manager:

git config --global --unset user.password

These ones are suggested when you use the Windows and macOS credential managers, respectively:

git config --global credential.helper wincred
git config --global credential.helper osxkeychain

If none of these work, see the original Stack Overflow post (WayBack Machine version archived here).

19.3 Getting started: cloning

To get started with a repository, you clone it to your local computer. Cloning a repository not only downloads all current versions of the files, but it downloads the entire repository - and therefore, also the complete history of the project. With large projects, this may become quite a large project. However, the advantage is that you can then locally inspect the entire history.

After you cloned the repository, you can make changes to the files and synchronize those with the repo. Note that sometimes, you may want to create a new repository. Although you can do this directory with Git, unless you know what you’re doing, you’ll probably want to initiate new repositories using a central repository suite such as GitLab. Therefore, see Chapter @ref{managing-a-gitlab-project} for instructions on how to initiate a new Git repo. Once you created a new repository at a GitLab server, you will still have to clone it to your local PC before you can start working with it.

Once you cloned a repository to your local machine, you can start working on the files it contains. If you want to try it out, you can open a Git Bash session in a directory where you’d like to clone a repository, and clone the repository containing this book by typing:

git clone https://gitlab.com/psy-ops/psy-ops-guide.git

For more information about cloning, see https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository.

19.4 Working on files in a repo: pulling, committing, and pushing

Working on files in a Git repository is no different from working on files that are not in a Git repository. Just use whichever software you prefer to make your edits. However, once you realised one or more changes (that are more or less coherent), you will want to package them up and synchronize them.

In Git, this is called committing the files to the repository. If you create a so-called commit, you basically tell Git to take all (or some) changes that were made to all (or some) of the files in the repository that Git tracks (and you can specify which files Git shouldn’t track, or in other words, which files Git should ignore: see section 19.6), and bundle them together in a package (i.e. a commit). Each commit has its own commit message, where you can (and have to; commit messages are not optional) explain what you did (and maybe why you did that).

To create a commit you have to first stage one or more files. If you work on a project that is very important and/or collaborate heavily using Git and/or have the discipline to keep finegrained documentation of what changes you make and why, you will probably not commit too many changes at once. However, in most cases, I just stage and then commit all changes I made in one go.

To stage all modified files (that includes created and deleted files and directories), use the following command:

git add .

To then specify a commit message, use:

git commit -m "This is a message"

This wraps all those changes up into one commit with that message associated to it.

Commits are created locally. In other words, you don’t need an internet connection - which also means that creating the commit, or a whole bunch of commits if you’re on a roll, does nothing to synchronize your local repository with the central one (and therefore, with the repo’s on everybody else’s machine).

To actually push the commits to the central repository, you have to literally tell Git to do that:

git push

Note that to push to a repository, you need to have be authorized to do so (see the Rights section above).

If there’s pushing, there’s also pulling. Where pushing sends all your commits to the remote repository, pulling downloads all changes from the remote repository that you didn’t yet have locally. In other words, if somebody else changed one or more files since the last time you told Git to pull, pulling will download those change to your computer, overwriting the old files.

You can pull with:

git pull

Because you will want to avoid working on an outdated version of a file, it is wise to always pull all recent changes into your local repository before you start working on anything. So, normally, you pull, work on files and stash your changes into one or more commits, and push either after each commit or set of commits, or when you’re done working in the repository for the day.

If you try to push commits to the remote repository, but the remote repository has been updated in the meantime, Git will refuse. It will tell you that the remote repository contains work you don’t have locally, and that you have to pull first. So, in that case you pull first. Sometimes, Git also refuses pulling: that happens if you made changes to one or more files that you did not yet stash in a commit yet, and that were also changed in the remote repository. In that case, you first have to make sure you create one or more commits with all changes, before you can pull the changes to your local machine. If the same parts of one or more files were changed, you have a merge conflict: you’ll have to tell Git which bits to use. This is discussed in section 19.7.

Commit messages have a useful extra functionality. If you use a Git repo management suite, such as GitLab (or GitHub), these often parse the commit messages and allow you to specify actions that they can then take. For example, in more complicated projects, in projects where an overview and structure and very important, or in projects that just involve people who really like an overview and structure, you may want to use issues, a feature of Git repo management suites. Issues allow you to keep track of tasks relating to the project, and GitLab has a so-called quick action that allows you to specify that a commit closes an issue. If you use this quick action in your commit message, GitLab will close the issue with a message that links to that commit. This is a very efficient way to keep track of progress in the project.

19.5 Commit changes and mistakes {fix-commit-mistakes}

19.5.1 Ammending a commit

Sometimes you want to change a commit (called “amending” in Git), for example because you hadn’t yet configured your username or you made a typo in the commit message.

To change the user making the commit, use:

git commit --amend --author="Author Name <email@address.com>"

To change the message, use:

git commit --amend -m "New Commit Message"

19.5.2 Committed to the wrong branch

Sometimes you accidentily commit to the wrong branch. To fix this, you can ‘turn back time’ a number of commits, switch to the right branch, and then commit there. To undo commits, use:

git reset HEAD~1

You can change the number following the tilde (~) to turn back time even further (i.e. more commits).

Then you can check out the right branch. For example:

git checkout the-correct-topic-branch

If there is no branch for your current work yet (i.e. no dedicated ‘topic branch’) you can create it on the fly by adding the -b argument:

git checkout -b the-new-and-correct-topic-branch

Then you just commit again, for example:

git add . ; git commit -m "I did some work"

19.5.3 Worked in the wrong branch without committing

If you already changed some files in the wrong branch, but didn’t commit yet, the solution is slightly different. You then “stash” your changes, which tells Git to store them temporarily while you switch to the right branch:

git stash

You can then switch to the right branch (optionally creating it on the fly using -b):

git checkout -b the-new-and-correct-topic-branch

And then you apply the changes from your stash:

git stash apply

19.6 Preventing files from syncing

You will usually not want to synchronize all files in a directory: projects may contain files including personal data (e.g. raw data) or secrets (e.g. passwords). Git allows you to specify the files to ignore in a file called “.gitignore”. Each line in the gitignore file specified a pattern, and all directories and files matching the pattern are ignored. Details are described in the relevant page of the Git manual.

_PRIVATE_

Allemaal dt-fouten corrigeren en andere typos en zorgen dat zinnen leesbaar worden enzo.

19.7 Merge conflicts

Normally, if you collaborate in a project, there will be some division of labour, making it unlikely that two people work on the same file at the same time. However, still you may sometimes run into situations where you did edit one or more lines in a file that somebody else edited at the same time.

In that case, probably just after Git forced you to pull recent changes from the remote repository, Git will present you with a merge conflict. In the files that were simultaneously edited locally (by you) and remotely (by somebody else who had already pushed their changes to the server), Git will insert both fragments (i.e. both versions of the conflicting lines), delimited bt three lines produced by Git.

Your job is now to manually merge both versions and by doing so, resolve each conflict. You do this simply by editing the file until it has the state you want it to have. You have three alternatives. First, you can select your version of the file content on those conflicting lines, in which case you remove the version produced by somebody else, as well as the three delimiting lines Git added. Second, you can retain the changes that the other person made, and remove your own version (as well as the three delimiting lines Git added). Or, third, you can really merge both versions into one ‘best of both worlds’ version, and then remove the three delimiting lines.

After you resolved all merge conflicts you stash those changes into a commit (or into several commits), and push them to the server.

19.8 Resolving merge conflicts

To see an overview of all files that currently have merge conflicts, use:

git diff --name-only --diff-filter=U

To unstage all files, in case you had staged it for committing before realising it had a merge conflict, use:

git reset

You can also specify the path to a file, if you just want to unstage one file:

git reset -- <path>

Replace <path> with the path to the file.

19.9 Renaming a repository

To rename a repository URL, change it in e.g. GitLab and then adjust it in your local Git repo using, for example:

git remote set-url origin https://gitlab.com/USERNAME/REPOSITORY.git

You can check the remote URL using:

git remote -v

19.10 Advanced: branching

Branches are ‘parallel universes’: copies of the same code, where you can make edits in one branch while the same code in the other branches remains untouched. When you switch between branches, Git updates the file in your repository to correspond to that branch. For example, if you switch to a branch, and then delete everything, and then commit that change, and then switch back to the previous branch, Git restores all files. After all, in that first branch, you didn’t delete anything.

Working with branches is useful when collaborating: one person can work on a bit of code without messing up the way things work for everybody else. And when you’re done with your change, you can merge your branch into the main branch (or whatever you branched off of) to combine the changes in both branches.

To add a branch called dev, you can use:

git checkout -b dev

This creates the branch and immediately checks it out (i.e. you’ll be working in that branch from that point on). This function is shorthand for two other commands:

git branch dev
git checkout dev

So to switch back to another branch, say main, you can use:

git checkout main

Note that after you created a branch, it only exists locally. To also create it in the remote repository, you can use (again using dev as an example):

git push --set-upstream origin dev

This tells git that your local branch dev corresponds to remote branch dev.

19.11 Advanced: rename a branch

Sometimes you want to rename a branch, such as master (see e.g. here or here. In this example, let’s assume we want to rename master to main, which seems to become the new default (other alternatives are e.g. prod, roughly in line with Vincent Driessen’s branching model, or trunk to extend the ‘tree’ metaphor used by Git’s branches). These instructions were taken from dev.to.

First check out the branch you want to rename:

git checkout master

Then rename it:

git branch -m master main

Then push that rename to the remote repository, setting the current branch to track the branch called main on the remote (because the current local branch (used to be master, now main) was still tracking the remote master branch):

git push -u origin main

Then log in to your remote upstream repository host (e.g. GitLab) and change the default branch (in GitLab: Settings > Repository > Default Branch).

Also make sure the branch you want to delete (e.g. master) is no longer a protected branch (in GitLab: Settings > Repository > Protected Branches).

Then you can delete that branch from the rmeote repository:

git push origin --delete master

And finally, do something called “Update the upstream remote’s HEAD” of which I don’t at present know what it is, but hope to learn one day:

git remote set-head origin -a

This all works if you’re the only one working with the repository. If others are working with it, as well, they will have to clone the repo again - or do more complicated stuff (see the original instructions at dev.to).