Difference between revisions of "Git Tutorials/Branching"
(Created page with "Git Tutorials: Branching<nowiki /> {{DISPLAYTITLE:Git Tutorial: Branching}}<nowiki /> {{Syllabus Git Tutorials}} Branching is an important feature o...") |
|||
(3 intermediate revisions by the same user not shown) | |||
Line 3: | Line 3: | ||
{{Syllabus Git Tutorials}} | {{Syllabus Git Tutorials}} | ||
− | Branching is an important feature of Git, which enables simultaneous work on different parts of a code with minimal interference between acting parties. As the name implies, branching refers to the creation of a separate line of repository tracking that branches of from your "main" repository (also called a branch, but can be understood as the trunk). When developing new features you do not necessarily want to directly <syntaxhighlight inline>commit</syntaxhighlight> your changes to "main", because they might interfere with your colleagues contribution of other features on a different branch. Even when differences do not directly interfere, they can still lead to a more cumbersome <syntaxhighlight inline>merge</syntaxhighlight> of both branches into "main", once all changes are complete. In larger projects this <syntaxhighlight inline>merge</syntaxhighlight> ''ing'' into the "main" branch is often blocked for most users. A so-called <syntaxhighlight inline>pull</syntaxhighlight> request must be made, so that a responsible person can <syntaxhighlight inline>pull</syntaxhighlight> your changes from your branch into "main". | + | [[File:Git_tutorial_-_branching.png|thumb|700px|Figure 1: Example of basic workflow with branching. The figure can show a local or a remote repository omitting depictions of commit and push operations to keep it simple (they happen somewhere on the dashed lines). Initially, two branches are created from the main branch. Two independent developments occur on those new branches visualized by the different colors green and orange. Theses branches can both belong to one or to two different developers. Once the green segment is finished, its contribution is merged into the main branch and work on the green segment branch stops. On the other side, the orange segment is developed simultaneously. When development is finished, a new branch is created from this branch. Instead of directly merging the orange branch into the main branch it continues development on some light purple features while in parallel some brown features are added to orange (e.g., solving bugs in orange code). Later, a merge operation combines the orange/brown and orange/purple branch together and subsequently those orange/brown/purple changes are merged into the main branch as well. At this point the main branch will contain contributions from all branches. Unlike the green branch, the orange/brown/purple branch continues to exist, because its purple features are still expanded and will later be merged again into the main branch.]] |
+ | |||
+ | Branching is an important feature of Git, which enables simultaneous work on different parts of a code with minimal interference between acting parties. As the name implies, branching refers to the creation of a separate line of repository tracking that branches of from your "main" repository (also called a branch, but can be understood as the trunk). When developing new features you do not necessarily want to directly <syntaxhighlight inline>commit</syntaxhighlight> your changes to "main", because they might interfere with your colleagues contribution of other features on a different branch. Even when differences do not directly interfere, they can still lead to a more cumbersome <syntaxhighlight inline>merge</syntaxhighlight> of both branches into "main", once all changes are complete. In larger projects this <syntaxhighlight inline>merge</syntaxhighlight> ''ing'' into the "main" branch is often blocked for most users. A so-called <syntaxhighlight inline>pull</syntaxhighlight> request must be made, so that a responsible person can <syntaxhighlight inline>pull</syntaxhighlight> your changes from your branch and <syntaxhighlight inline>merge</syntaxhighlight> them into "main". | ||
To view all existing branches use the <syntaxhighlight inline>git branch --list</syntaxhighlight> command: | To view all existing branches use the <syntaxhighlight inline>git branch --list</syntaxhighlight> command: | ||
− | <syntaxhighlight lang=" | + | <syntaxhighlight lang="console" line> |
user@HPC.NRW:~$ git branch --list | user@HPC.NRW:~$ git branch --list | ||
* main | * main | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | We can see that currently the project contains only one branch called "main" (line 2). The "*" next to it is used to mark our current branch. | + | We can see that currently the project contains only one branch called "main" (line 2). The "*" next to it is used to mark our current branch. Before we continue with the tutorial, a look at Figure 1 might be helpful. It visualizes the basic idea behind branching within a repository. Moreover, we can see the similarities between the relation of other branches to the main branch and the relation of a local repository to the remote repository. |
== Handling Branches == | == Handling Branches == | ||
Creating a new branch is as easy as entering <syntaxhighlight inline>git branch <name of branch></syntaxhighlight>: | Creating a new branch is as easy as entering <syntaxhighlight inline>git branch <name of branch></syntaxhighlight>: | ||
− | <syntaxhighlight lang=" | + | <syntaxhighlight lang="console" line> |
user@HPC.NRW:~$ git branch branchingTut | user@HPC.NRW:~$ git branch branchingTut | ||
user@HPC.NRW:~$ git branch --list | user@HPC.NRW:~$ git branch --list | ||
Line 28: | Line 30: | ||
To switch to a different branch we use <syntaxhighlight inline>git checkout <name of branch></syntaxhighlight>: | To switch to a different branch we use <syntaxhighlight inline>git checkout <name of branch></syntaxhighlight>: | ||
− | <syntaxhighlight lang=" | + | <syntaxhighlight lang="console" line> |
user@HPC.NRW:~$ git checkout branchingTut | user@HPC.NRW:~$ git checkout branchingTut | ||
M someFile | M someFile | ||
Line 38: | Line 40: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 2 informs us about a <syntaxhighlight inline>M</syntaxhighlight>odified file named "someFile". This can happen if we make changes to our repository (like <syntaxhighlight inline>A</syntaxhighlight>dding or <syntaxhighlight inline>M</syntaxhighlight>odifying files), but do not <syntaxhighlight inline>commit</syntaxhighlight> them before switching the branch. Naturally, this also happens when we want to switch to a branch with different contents than in hour current branch. | Line 2 informs us about a <syntaxhighlight inline>M</syntaxhighlight>odified file named "someFile". This can happen if we make changes to our repository (like <syntaxhighlight inline>A</syntaxhighlight>dding or <syntaxhighlight inline>M</syntaxhighlight>odifying files), but do not <syntaxhighlight inline>commit</syntaxhighlight> them before switching the branch. Naturally, this also happens when we want to switch to a branch with different contents than in hour current branch. | ||
− | Keep in mind that a <syntaxhighlight inline>git checkout <name of branch></syntaxhighlight> is not like a <syntaxhighlight inline>git clone</syntaxhighlight> operation and therefore does not make a full copy of a different code branch. Instead, it tracks the <syntaxhighlight inline>commit</syntaxhighlight> history of the chosen branch and reproduces the changes in | + | Keep in mind that a <syntaxhighlight inline>git checkout <name of branch></syntaxhighlight> is not like a <syntaxhighlight inline>git clone</syntaxhighlight> operation and therefore does not make a full copy of a different code branch. Instead, it tracks the <syntaxhighlight inline>commit</syntaxhighlight> history of the chosen branch and reproduces the changes in our repository. Any modifications we <syntaxhighlight inline>commit</syntaxhighlight> now will be added to that branch. |
− | {| role="presentation" class="wikitable mw-collapsible mw-collapsed" style="width:62em" data-expandtext="▼" data-collapsetext="▲" | + | To delete a branch we type {{Avoid wrap|<syntaxhighlight inline>git branch -d <name of branch></syntaxhighlight>}}. However, this will not be possible if the branch contains uncommitted changes. We either have to perform a <syntaxhighlight inline>commit</syntaxhighlight> first or use the option <syntaxhighlight inline>-D</syntaxhighlight> instead of <syntaxhighlight inline>-d</syntaxhighlight>. If we need to restore a deleted branch we can use {{Avoid wrap | <syntaxhighlight inline>git checkout -b <new/old name of branch> <SHA of its last commit></syntaxhighlight>}}. The SHA-value has been touched upon, [[Git_Tutorials/Creating_and_Changing_Repositories#Going back to a previous Version|previously]]. |
+ | |||
+ | {| role="presentation" class="wikitable mw-collapsible mw-collapsed" style="background-color:white; width:62em" data-expandtext="▼" data-collapsetext="▲" | ||
|+ style="background-color:#2e3871; color:white;text-align: center; width:10em"| <strong>Further remarks</strong> | |+ style="background-color:#2e3871; color:white;text-align: center; width:10em"| <strong>Further remarks</strong> | ||
|- | |- | ||
Line 47: | Line 51: | ||
| Usually, a branch is created from the most recent commit of the branch you are currently on. If we want to create it from a different branch (and switch to it), we will have to use {{Avoid wrap|<syntaxhighlight inline>git checkout -b <name of new branch> <name of different branch></syntaxhighlight>}}. | | Usually, a branch is created from the most recent commit of the branch you are currently on. If we want to create it from a different branch (and switch to it), we will have to use {{Avoid wrap|<syntaxhighlight inline>git checkout -b <name of new branch> <name of different branch></syntaxhighlight>}}. | ||
|- | |- | ||
− | | | + | | Deleting a branch as shown above only deletes its local version. A remote branch has to be deleted this way: {{Avoid wrap|<syntaxhighlight inline>git push <name of remote repository> --delete <name of remote branch></syntaxhighlight>}} |
|- | |- | ||
| Renaming our current branch is done through the command {{Avoid wrap|<syntaxhighlight inline>git branch -m <new name of branch></syntaxhighlight>}}. | | Renaming our current branch is done through the command {{Avoid wrap|<syntaxhighlight inline>git branch -m <new name of branch></syntaxhighlight>}}. | ||
|} | |} | ||
+ | Sometimes we want to switch to a different branch, but we have ''un''<syntaxhighlight inline>commit</syntaxhighlight>''ted'' changes in the current branch. In such a case, Git will not allow us to switch. If we do not want to perform a <syntaxhighlight inline>commit</syntaxhighlight> we can use the <syntaxhighlight inline>git stash</syntaxhighlight> functionality. With it we can safely put away all changes since the last <syntaxhighlight inline>commit</syntaxhighlight>. Afterwards, we can restore what has been put into the stash with {{Avoid wrap |<syntaxhighlight inline>git stash pop</syntaxhighlight>}}. Here is an example: | ||
+ | |||
+ | <syntaxhighlight lang="console" line> | ||
+ | otherUser@HPC.NRW:~$ git checkout main | ||
+ | error: Your local changes to the following files would be overwritten by checkout: | ||
+ | Branching | ||
+ | Please commit your changes or stash them before you switch branches. | ||
+ | Aborting | ||
+ | otherUser@HPC.NRW:~$ git stash | ||
+ | Saved working directory and index state WIP on branchingTut: 802006b Add branch deletion to conflictless merge | ||
+ | otherUser@HPC.NRW:~$ git checkout main | ||
+ | Switched to branch 'main' | ||
+ | Your branch is ahead of 'origin/main' by 15 commits. | ||
+ | (use "git push" to publish your local commits) | ||
+ | otherUser@HPC.NRW:~$ git checkout branchingTut | ||
+ | Switched to branch 'branchingTut' | ||
+ | otherUser@HPC.NRW:~$ git stash pop | ||
+ | On branch branchingTut | ||
+ | Changes not staged for commit: | ||
+ | (use "git add <file>..." to update what will be committed) | ||
+ | (use "git restore <file>..." to discard changes in working directory) | ||
+ | modified: Branching | ||
+ | |||
+ | no changes added to commit (use "git add" and/or "git commit -a") | ||
+ | Dropped refs/stash@{0} (fe7bb4e2c792a6dcecd4e576a22a6640410cb078) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | It is possible to have multiple stashes at once and even to apply them to other branches and much more. If you are interested we suggest Git's easy to understand [https://git-scm.com/book/sv/v2/Git-Tools-Stashing-and-Cleaning section on working with stashes]. | ||
== Remote Branches == | == Remote Branches == | ||
− | If others are supposed to have access to our branch (or we want to have access from different locations), we need to make sure that it also exists in the remote repository by entering <syntaxhighlight inline>git push <name of remote repository> <name of branch></syntaxhighlight>: | + | If others are supposed to have access to our branch (or we want to have access from different locations), we need to make sure that it also exists in the remote repository by entering {{Avoid wrap | <syntaxhighlight inline>git push <name of remote repository> <name of branch></syntaxhighlight>}}: |
− | <syntaxhighlight lang=" | + | <syntaxhighlight lang="console" line> |
user@HPC.NRW:~$ git push origin branchingTut | user@HPC.NRW:~$ git push origin branchingTut | ||
Username for 'https://github.com': HPC.NRW-User | Username for 'https://github.com': HPC.NRW-User | ||
Line 84: | Line 116: | ||
A remote <syntaxhighlight inline>checkout</syntaxhighlight> works the same way a local <syntaxhighlight inline>checkout</syntaxhighlight> does. The main difference is that your local repository might not be up to date with remote branch information, which is why we might miss updates on existing branches (as well as respective <syntaxhighlight inline>git log</syntaxhighlight> data). Below, you can see Git output for a user who cloned our repository after the first tutorial, but before branching was introduced: | A remote <syntaxhighlight inline>checkout</syntaxhighlight> works the same way a local <syntaxhighlight inline>checkout</syntaxhighlight> does. The main difference is that your local repository might not be up to date with remote branch information, which is why we might miss updates on existing branches (as well as respective <syntaxhighlight inline>git log</syntaxhighlight> data). Below, you can see Git output for a user who cloned our repository after the first tutorial, but before branching was introduced: | ||
− | <syntaxhighlight lang=" | + | <syntaxhighlight lang="console"> |
otherUser@HPC.NRW:~$ git branch -a | otherUser@HPC.NRW:~$ git branch -a | ||
* main | * main | ||
Line 93: | Line 125: | ||
In order to see the newest changes in the remote repository, they need to use the <syntaxhighlight inline>fetch</syntaxhighlight> command: | In order to see the newest changes in the remote repository, they need to use the <syntaxhighlight inline>fetch</syntaxhighlight> command: | ||
− | <syntaxhighlight lang=" | + | <syntaxhighlight lang="console" line> |
otherUser@HPC.NRW:~$ git fetch | otherUser@HPC.NRW:~$ git fetch | ||
Username for 'https://github.com': HPC.NRW-otherUser | Username for 'https://github.com': HPC.NRW-otherUser | ||
Line 112: | Line 144: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | Line 10 informs them that a new branch exists in the remote repository. Lines 12 to 16 show that they can see the "branchingTut" branch, however, they still do not have a local version of it. Using <syntaxhighlight inline>git checkout | + | Line 10 informs them that a new branch exists in the remote repository. Lines 12 to 16 show that they can see the "branchingTut" branch, however, they still do not have a local version of it. Using <syntaxhighlight inline>git checkout branchingTut</syntaxhighlight> they can then create a local branch based on the remote one. As the branch already exists in the remote repository, the option <syntaxhighlight inline>-b</syntaxhighlight> is not required for branch creation using <syntaxhighlight inline>checkout</syntaxhighlight>. |
− | {| role="presentation" class="wikitable mw-collapsible mw-collapsed" style="width: | + | {| role="presentation" class="wikitable mw-collapsible mw-collapsed" style="background-color:white; width:54em" data-expandtext="▼" data-collapsetext="▲" |
|+ style="background-color:#2e3871; color:white;text-align: center; width:10em"| <strong>Further remarks</strong> | |+ style="background-color:#2e3871; color:white;text-align: center; width:10em"| <strong>Further remarks</strong> | ||
|- | |- | ||
|It is possible to display updated remote repository information without actually updating:{{Avoid wrap|<syntaxhighlight inline>git ls-remote origin</syntaxhighlight>}} | |It is possible to display updated remote repository information without actually updating:{{Avoid wrap|<syntaxhighlight inline>git ls-remote origin</syntaxhighlight>}} | ||
|- | |- | ||
− | |If we have several remote repositories, we need to further specify the desired branch: <syntaxhighlight inline>git checkout -b <name of branch> <name of remote repository>/<name of remote branch> </syntaxhighlight> | + | |<syntaxhighlight inline>git fetch <name of remote branch></syntaxhighlight> can be used to only update information on one particular remote branch. |
+ | |- | ||
+ | |If more than one remote repository is available, {{Avoid wrap|<syntaxhighlight inline>git remote update</syntaxhighlight>}} will update information on all of them. | ||
+ | |- | ||
+ | |If we have several remote repositories, we need to further specify the desired branch: {{Avoid wrap|<syntaxhighlight inline>git checkout -b <name of branch> <name of remote repository>/<name of remote branch> </syntaxhighlight>}} | ||
|- | |- | ||
|<syntaxhighlight inline>git checkout</syntaxhighlight> is actually more versatile and not only used for switching or restoring branches. Therefore, beginning with version 2.23, Git introduced {{Avoid wrap|<syntaxhighlight inline>git switch <name of existing branch></syntaxhighlight>}} for switching to a different branch. | |<syntaxhighlight inline>git checkout</syntaxhighlight> is actually more versatile and not only used for switching or restoring branches. Therefore, beginning with version 2.23, Git introduced {{Avoid wrap|<syntaxhighlight inline>git switch <name of existing branch></syntaxhighlight>}} for switching to a different branch. | ||
|- | |- | ||
|<syntaxhighlight inline>git switch -c <name of new branch></syntaxhighlight> creates a new branch and switches to it. | |<syntaxhighlight inline>git switch -c <name of new branch></syntaxhighlight> creates a new branch and switches to it. | ||
+ | |- | ||
+ | |If you <syntaxhighlight inline>switch</syntaxhighlight> to a local branch that has further developed than its remote version, Git will inform you: | ||
+ | <syntaxhighlight lang="console"> | ||
+ | otherUser@HPC.NRW:~$ git switch main | ||
+ | Switched to branch 'main' | ||
+ | Your branch is ahead of 'origin/main' by 2 commits. | ||
+ | (use "git push" to publish your local commits) | ||
+ | </syntaxhighlight> | ||
|- | |- | ||
|<syntaxhighlight inline>git switch</syntaxhighlight> is still marked as experimental in the Git documentation, which is why we only mention it here. | |<syntaxhighlight inline>git switch</syntaxhighlight> is still marked as experimental in the Git documentation, which is why we only mention it here. | ||
|} | |} | ||
+ | == Merging Branches == | ||
+ | |||
+ | Once work on a branch has progressed sufficiently, the changes can be <syntaxhighlight inline>merge</syntaxhighlight>''d'' into "main", i.e. that "main" will contain all changes and additions from the other branch. If the changes on the branch concern a separate file or new section in existing files, the <syntaxhighlight inline>merge</syntaxhighlight> can be performed without any additional work. However, sometimes merge conflicts can occure, for example when two branches want to modify the same code sections or when "main" has changed after a branch has been created from it. We will cover both scenarios beginning with the easy one, where everything works fine. | ||
+ | |||
+ | Generally, <syntaxhighlight inline>git merge <name of branch to merge></syntaxhighlight> combines the given branch into our currently active branch, but first we need some content to <syntaxhighlight inline>merge</syntaxhighlight>. For a conflict free <syntaxhighlight inline>merge</syntaxhighlight> we will do the following: | ||
+ | |||
+ | # Switch to "main" | ||
+ | # Make sure it is locally up to date | ||
+ | # Create an new branch "tempBranch" out of "main" | ||
+ | # Create a new file "tempFile" and add it to "tempBranch" | ||
+ | # Merge "tempBranch" into "main", so that "tempFile" can also be found in "main" | ||
+ | # Write something into the first line of "tempFile" on "main" | ||
+ | # Write the same thing into the first line of "tempFile" on "tempBranch" | ||
+ | # Write something into the second line of "tempFile" on "tempBranch" | ||
+ | # Merge "tempBranch" into "main" again | ||
+ | # Remove "tempBranch" | ||
+ | |||
+ | Above, we have purposefully not mentioned any <syntaxhighlight inline>commit</syntaxhighlight> operations. Try it yourself or check the solution below. | ||
+ | |||
+ | {| role="presentation" class="wikitable mw-collapsible mw-collapsed" style="background-color:white; width:70em" data-expandtext="▼" data-collapsetext="▲" | ||
+ | |+ style="background-color:#2e3871; color:white;text-align: center; width:10em"| <strong>Solution and remarks</strong> | ||
+ | |- | ||
+ | | For the sake of improved readability we have added some blank lines in the commands and output below. | ||
+ | Remarks to some lines can be found at the bottom. | ||
+ | <syntaxhighlight lang="console" line> | ||
+ | user@HPC.NRW:~$ git switch main | ||
+ | Switched to branch 'main' | ||
+ | Your branch is up to date with 'origin/main'. | ||
+ | user@HPC.NRW:~$ git checkout -b tempBranch | ||
+ | Switched to a new branch 'tempBranch' | ||
+ | |||
+ | user@HPC.NRW:~$ touch tempFile | ||
+ | |||
+ | user@HPC.NRW:~$ git add tempFile | ||
+ | |||
+ | user@HPC.NRW:~$ git commit -m "Add tempFile to tempBranch" | ||
+ | [tempBranch b6bc099] Add tempFile to tempBranch | ||
+ | 1 file changed, 0 insertions(+), 0 deletions(-) | ||
+ | create mode 100644 tempFile | ||
+ | user@HPC.NRW:~$ git checkout main | ||
+ | Switched to branch 'main' | ||
+ | Your branch is up to date with 'origin/main'. | ||
+ | |||
+ | user@HPC.NRW:~$ git merge -m "Merge tempBranch into main" tempBranch | ||
+ | Updating 7ea3b70..b6bc099 | ||
+ | Fast-forward (no commit created; -m option ignored) | ||
+ | tempFile | 0 | ||
+ | 1 file changed, 0 insertions(+), 0 deletions(-) | ||
+ | create mode 100644 tempFile | ||
+ | |||
+ | user@HPC.NRW:~$ echo "firstLine" >> tempFile | ||
+ | user@HPC.NRW:~$ git add tempFile | ||
+ | user@HPC.NRW:~$ git commit -m "Write in first line of tempFile" | ||
+ | [main 2a4a1d6] Write in first line of tempFile | ||
+ | 1 file changed, 1 insertion(+) | ||
+ | |||
+ | user@HPC.NRW:~$ git switch tempBranch | ||
+ | Switched to branch 'tempBranch' | ||
+ | |||
+ | user@HPC.NRW:~$ touch tempFile; echo "firstLine" >> tempFile; echo "secondLine" >> tempFile; cat tempFile | ||
+ | firstLine | ||
+ | secondLine | ||
+ | user@HPC.NRW:~$ git add tempFile ; git commit -m "Write in second line of tempFile" | ||
+ | [tempBranch ee1bb34] Write in second line of tempFile | ||
+ | 1 file changed, 3 insertions(+) | ||
+ | |||
+ | user@HPC.NRW:~$ git switch main; cat tempFile | ||
+ | Switched to branch 'main' | ||
+ | Your branch is ahead of 'origin/main' by 2 commits. | ||
+ | (use "git push" to publish your local commits) | ||
+ | firstLine | ||
+ | |||
+ | user@HPC.NRW:~$ git merge -m "Merge with tempBranch without conflicts" tempBranch | ||
+ | Merge made by the 'recursive' strategy. | ||
+ | tempFile | 1 + | ||
+ | 1 file changed, 1 insertion(+) | ||
+ | |||
+ | user@HPC.NRW:~$ cat tempFile | ||
+ | firstLine | ||
+ | secondLine | ||
+ | |||
+ | user@HPC.NRW:~$ git log --oneline | ||
+ | 20e8d8f (HEAD -> main) Merge with tempBranch without conflicts | ||
+ | 8aa439f (tempBranch) Write in second line of tempFile | ||
+ | |||
+ | user@HPC.NRW:~$ git switch branchingTut | ||
+ | Switched to branch 'branchingTut' | ||
+ | user@HPC.NRW:~$ git branch -d tempBranch | ||
+ | error: The branch 'tempBranch' is not fully merged. | ||
+ | If you are sure you want to delete it, run 'git branch -D tempBranch'. | ||
+ | |||
+ | user@HPC.NRW:~$ git switch main | ||
+ | Switched to branch 'main' | ||
+ | Your branch is ahead of 'origin/main' by 15 commits. | ||
+ | (use "git push" to publish your local commits) | ||
+ | user@HPC.NRW:~$ git branch -d tempBranch | ||
+ | Deleted branch tempBranch (was 8a4439f). | ||
+ | |||
+ | user@HPC.NRW:~$ git branch -a | ||
+ | branchingTut | ||
+ | creatingAndChangingRepositoriesTut | ||
+ | * main | ||
+ | remotes/origin/branchingTut | ||
+ | remotes/origin/creatingAndChangingRepositoriesTut | ||
+ | remotes/origin/main | ||
+ | </syntaxhighlight> | ||
+ | |- | ||
+ | |Some remarks to the different lines: | ||
+ | * line '''3''': When switching to a different branch a message is shown whether the local branch is up to date with the remote one, or if it is ahead of it | ||
+ | * lines '''11, 28, 38''': Keep in mind that only <syntaxhighlight inline>commit</syntaxhighlight>''ted'' changes can be merged into a branch, as everything that is not <syntaxhighlight inline>commit</syntaxhighlight>''ted'' is not part of the repository and only lies in your working space. | ||
+ | * line '''15''': Same as line 1, but <syntaxhighlight inline>git checkout</syntaxhighlight> is used instead of <syntaxhighlight inline>git switch</syntaxhighlight>. | ||
+ | * lines '''19 - 23''': The option <syntaxhighlight inline>-m</syntaxhighlight> is used to directly provide a message to the automatic <syntaxhighlight inline>commit</syntaxhighlight> after a <syntaxhighlight inline>merge</syntaxhighlight>. This is usually an option to omit a subsequent <syntaxhighlight inline>commit</syntaxhighlight>, when a merge conflict will not arise. However, here it is unnecessary, because no file has been changed (only <syntaxhighlight inline>add</syntaxhighlight>''ed'' for tracking). As can be seen in line 21, the option is ignored. | ||
+ | * line '''24''': File "tempFile" is created in the branch "main" in accordance with the <syntaxhighlight inline>merge</syntaxhighlight>. | ||
+ | * lines '''46 & 47, 54 & 55''': Initially, "tempFile" did only contain the first line with "firstLine" on branch "main". After the <syntaxhighlight inline>merge</syntaxhighlight> operation, it does also contain the second line with "secondLine" from branch "tempBranch". | ||
+ | * lines '''48 - 51''': Like in line 19, a <syntaxhighlight inline>commit</syntaxhighlight> message is provided to <syntaxhighlight inline>git merge</syntaxhighlight> via the option <syntaxhighlight inline>-m</syntaxhighlight>. Unlike in line 19, the <syntaxhighlight inline>commit</syntaxhighlight> is actually performed, as a file ("tempFile") was modified by the <syntaxhighlight inline>merge</syntaxhighlight>. | ||
+ | * lines '''57 - 59''': The log of branch "main" shows the <syntaxhighlight inline>merge</syntaxhighlight> message in line 58 and the last <syntaxhighlight inline>commit</syntaxhighlight> from branch "tempBranch" (executed in line 38). | ||
+ | * lines '''61 - 65''': Here a deletion of the branch is tried while being on branch "branchingTut". This yields an error, because ''un''<syntaxhighlight inline>merge</syntaxhighlight>''d'' changes might be lost, as the <syntaxhighlight inline>merge</syntaxhighlight> happened on branch "main". Further, line 65 mentions the forced delete using option <syntaxhighlight inline>-D</syntaxhighlight>, which was mentioned in the beginning of the branching tutorial. | ||
+ | * lines '''67 - 72''': Here the deletion of "branchingTut" works without problems, because its changes have been <syntaxhighlight inline>merge</syntaxhighlight>''d'' into our current branch "main". This shows, that usually Git takes the current branch as a reference for its operations. | ||
+ | * line '''69''': Please note that the local repository is 15 <syntaxhighlight inline>commit</syntaxhighlight>''s'' ahead of the remote repository. You should not clutter the remote repository with unnecessary <syntaxhighlight inline>push</syntaxhighlight>''es''. | ||
+ | * line '''72''': Information regarding successful deletion of branch and its SHA (8a4439f), which can be used later to retrieve the branch through a <syntaxhighlight inline>checkout</syntaxhighlight>. | ||
+ | * lines '''74 - 80''': Branch "tempBranch" does not appear in our list of branches anymore. | ||
+ | |} | ||
+ | |||
+ | Next, we will create a <syntaxhighlight inline>merge</syntaxhighlight> '''with''' a conflict. As mentioned before, a conflict can occur when two users try to modify the same parts of a code, because it becomes unclear which changes have to be incorporated into the <syntaxhighlight inline>merge</syntaxhighlight> and which not. To reproduce such a situation, we can mostly repeat the steps from the previous <syntaxhighlight inline>merge</syntaxhighlight> without conflicts. Here are some instructions with a solution and remarks below: | ||
+ | |||
+ | # Create two branches "tempBranch" and "toMergeBranch" (does not matter of which branch) | ||
+ | # Create a file "tempFile" on both branches | ||
+ | # Write something in the first line of "tempFile" on "tempBranch" | ||
+ | # Write something in the second line of "tempFile" on "toMergeBranch" | ||
+ | # Merge "toMergeBranch" into "tempBranch" providing a merge message | ||
+ | # Resolve the conflict (see conflict solving basics below) | ||
+ | # Delete branches "tempBranch" and "toMergeBranch" | ||
+ | |||
+ | {| role="presentation" class="wikitable mw-collapsible mw-collapsed" style="background-color:white; width:70em" data-expandtext="▼" data-collapsetext="▲" | ||
+ | |+ style="background-color:#2e3871; color:white;text-align: center; width:10em"| <strong>Solution and remarks</strong> | ||
+ | |- | ||
+ | | For the sake of improved readability we have added some blank lines in the commands and output below. | ||
+ | Remarks to some lines can be found at the bottom. | ||
+ | <syntaxhighlight lang="console" line> | ||
+ | user@HPC.NRW:~$ git branch -a | ||
+ | * branchingTut | ||
+ | creatingAndChangingRepositoriesTut | ||
+ | main | ||
+ | remotes/origin/branchingTut | ||
+ | remotes/origin/creatingAndChangingRepositoriesTut | ||
+ | remotes/origin/main | ||
+ | |||
+ | user@HPC.NRW:~$ git branch tempBranch | ||
+ | user@HPC.NRW:~$ git branch toMergeBranch | ||
+ | |||
+ | user@HPC.NRW:~$ git switch tempBranch | ||
+ | M Branching | ||
+ | Switched to branch 'tempBranch' | ||
+ | |||
+ | user@HPC.NRW:~$ rm -r * | ||
+ | |||
+ | user@HPC.NRW:~$ touch tempFile; echo "write in first line" >> tempFile | ||
+ | user@HPC.NRW:~$ git add . | ||
+ | user@HPC.NRW:~$ git commit -m "Delete some files in branch and write tempFile" | ||
+ | [tempBranch 8385a37] Delete some files in branch and write tempFile | ||
+ | 5 files changed, 1 insertion(+), 620 deletions(-) | ||
+ | delete mode 100644 Branching | ||
+ | delete mode 100644 Creating_and_Changing_Repositories | ||
+ | delete mode 100644 Git_Tutorials | ||
+ | delete mode 100644 clonedRepoFile | ||
+ | create mode 100644 tempFile | ||
+ | |||
+ | user@HPC.NRW:~$ git switch toMergeBranch | ||
+ | Switched to branch 'toMergeBranch' | ||
+ | user@HPC.NRW:~$ rm -r * | ||
+ | user@HPC.NRW:~$ touch tempFile; echo "" >> tempFile; echo "something in second line" >> tempFile | ||
+ | user@HPC.NRW:~$ cat tempFile | ||
+ | |||
+ | something in second line | ||
+ | user@HPC.NRW:~$ git add . | ||
+ | user@HPC.NRW:~$ git commit -m "Delete some files on branch and write tempFile second line" | ||
+ | [toMergeBranch 529b936] Delete some files on branch and write tempFile second line | ||
+ | 5 files changed, 2 insertions(+), 620 deletions(-) | ||
+ | delete mode 100644 Branching | ||
+ | delete mode 100644 Creating_and_Changing_Repositories | ||
+ | delete mode 100644 Git_Tutorials | ||
+ | delete mode 100644 clonedRepoFile | ||
+ | create mode 100644 tempFile | ||
+ | |||
+ | user@HPC.NRW:~$ git switch tempBranch | ||
+ | Switched to branch 'tempBranch' | ||
+ | user@HPC.NRW:~$ git merge -m "merge toMergeBranch into tempBranch" toMergeBranch | ||
+ | CONFLICT (add/add): Merge conflict in tempFile | ||
+ | Auto-merging tempFile | ||
+ | Automatic merge failed; fix conflicts and then commit the result. | ||
+ | |||
+ | user@HPC.NRW:~$ ls .git/ | ||
+ | branches/ config FETCH_HEAD hooks/ info/ MERGE_HEAD MERGE_MSG ORIG_HEAD refs/ | ||
+ | COMMIT_EDITMSG description HEAD index logs/ MERGE_MODE objects/ packed-refs | ||
+ | |||
+ | user@HPC.NRW:~$ vi tempFile | ||
+ | user@HPC.NRW:~$ git add tempFile | ||
+ | user@HPC.NRW:~$ git commit -m "Merge with toMergeBranch" | ||
+ | [tempBranch 5e72b28] Merge with toMergeBranch | ||
+ | |||
+ | user@HPC.NRW:~$ ls .git/ | ||
+ | branches/ config FETCH_HEAD hooks/ info/ objects/ packed-refs | ||
+ | COMMIT_EDITMSG description HEAD index logs/ ORIG_HEAD refs/ | ||
+ | |||
+ | user@HPC.NRW:~$ cat tempFile | ||
+ | write in first line | ||
+ | something in second line | ||
+ | |||
+ | user@HPC.NRW:~$ git switch branchingTut | ||
+ | Switched to branch 'branchingTut' | ||
+ | |||
+ | user@HPC.NRW:~$ git branch -d tempBranch | ||
+ | error: The branch 'tempBranch' is not fully merged. | ||
+ | If you are sure you want to delete it, run 'git branch -D tempBranch'. | ||
+ | |||
+ | user@HPC.NRW:~$ git branch -D tempBranch | ||
+ | Deleted branch tempBranch (was 5e72b28). | ||
+ | user@HPC.NRW:~$ git branch -D toMergeBranch | ||
+ | Deleted branch toMergeBranch (was 529b936). | ||
+ | </syntaxhighlight> | ||
+ | * line '''1''': We create the branches while on branch "branchingTut", i.e. the new branches will have the corresponding files and we might want to delete them for this tutorial. | ||
+ | * line '''16''': Here we delete those unnecessary files. Do not worry, files and directories beginning with a dot (like ''.gitignore'') are not affected by this. | ||
+ | * lines '''33 - 35''': File "tempFile" on branch "toMergeBranch" contains one empty line and some text in the second line. | ||
+ | * lines '''48 -51''': We get a merge conflict that cannot be automatically resolved (line 51) and therefore the <syntaxhighlight inline>merge</syntaxhighlight> message gets ignored. The conflict arises from branch "tempBranch" having text in the first line of "tempFile" while branch "toMergeBranch" has nothing in the first line. Having text in the second line of "tempFile" on branch "toMergeBranch" causes no conflict, as this does not directly contradict "tempBranch", which has no information about contents of the second line of "tempFile" (it does not exist there). | ||
+ | * lines '''53 - 55''': Here we show the <syntaxhighlight inline>merge</syntaxhighlight> related files ''MERGE_HEAD'', ''MERGE_MODE'' and ''MERGE_MSG'' existing due to a conflict. ''MERGE_MSG'' contains information on the conflict causing files. | ||
+ | * line '''57''': Here we edit the conflicting file. More information on the that are provided further below. | ||
+ | * lines '''57 - 60''': These are the actual lines solving the <syntaxhighlight inline>merge</syntaxhighlight> conflict. | ||
+ | * lines '''62 - 64''': The conflict related files have disappeared now (compare with comment on lines 53 - 55). | ||
+ | * lines '''73 - 75''': "tempBranch" and "toMergeBranch" have been created from "brachingTut", but have not been <syntaxhighlight inline>merge</syntaxhighlight>''d'' into it. Therefore, we get an error when trying to delete them with option <syntaxhighlight inline>-d</syntaxhighlight>. | ||
+ | * lines '''77 - 80''': Option <syntaxhighlight inline>-D</syntaxhighlight> is the solution here. SHA values of deleted branches are shown. | ||
+ | |} | ||
+ | |||
+ | Below we provide an example for a file containing a merge conflict. In this case, it is the file "tempFile" from the exercise above. | ||
+ | <syntaxhighlight lang="text"> | ||
+ | <<<<<<< HEAD | ||
+ | write in first line | ||
+ | ======= | ||
+ | |||
+ | something in second line | ||
+ | >>>>>>> toMergeBranch | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | The <syntaxhighlight inline><<<<<<< HEAD</syntaxhighlight> marks the beginning of a difference or conflict in the file on our current branch (the branch we wanted to <syntaxhighlight inline>merge</syntaxhighlight> into), <syntaxhighlight inline>=======</syntaxhighlight> separates this content from the content in the branch we wanted to <syntaxhighlight inline>merge</syntaxhighlight>. Therefore, everything between <syntaxhighlight inline>=======</syntaxhighlight> and <syntaxhighlight inline>>>>>>>> toMergeBranch</syntaxhighlight> is the corresponding content on branch "toMergeBranch". Anything outside of those markers is identical on both branches. Further, those markers can appear multiple times in one file, should several conflicts exist. | ||
+ | An easy way to check which files contain conflicts is to look into <syntaxhighlight inline>./.git/MERGE_MSG</syntaxhighlight>. This file is only present while a <syntaxhighlight inline>merge</syntaxhighlight> conflict persists. | ||
== Best Practices == | == Best Practices == | ||
+ | * Always start a new branch when working on a new feature and <syntaxhighlight inline>merge</syntaxhighlight> your changes and additions into the main branch, once the work is finished. | ||
+ | * Common branches are: main, develop, feature, release, hotfix. Naturally, those branches can have branches as well, e.g., when multiple features are being developed at the same time. However, depending on your environment, completely different naming conventions can be sensible. | ||
+ | * Several strategies exist on the structured creation of branches and branching conventions. [[Git_Tutorials/CI/CD|We cover them here]]. | ||
== Glossary == | == Glossary == | ||
+ | |||
+ | * <syntaxhighlight inline>branch</syntaxhighlight>: Used for branch operations (e.g., creation or deletion). | ||
+ | * <syntaxhighlight inline>switch</syntaxhighlight>: Similar to <syntaxhighlight inline>checkout</syntaxhighlight>, but reserved for switching branches. New feature, so functionality might change. | ||
+ | * <syntaxhighlight inline>fetch</syntaxhighlight>: Used to update information on remote repositories (and branches). | ||
+ | * <syntaxhighlight inline>merge</syntaxhighlight>: Used to combine one branch into another. | ||
+ | * <syntaxhighlight inline>stash</syntaxhighlight>: Used to temporarily store away ''un''<syntaxhighlight inline>commit</syntaxhighlight>''ted'' changes. Can be retrieved with a <syntaxhighlight inline>git stash pop</syntaxhighlight>. | ||
== Useful Links == | == Useful Links == | ||
+ | |||
+ | * [https://lab.github.com/githubtraining/introduction-to-github Interactive GitHub course]: Introduction and basic branch operations | ||
+ | * [https://www.researchgate.net/publication/359405202_Guidelines_for_collaborative_development_of_sustainable_data_treatment_software Paper on aspects of RSE]: Section 3.2.3 contains information on different branching workflows and where they are employed. | ||
+ | * [https://git-scm.com/book/sv/v2/Git-Tools-Stashing-and-Cleaning Git documentary]: Further information on the possibilities of <syntaxhighlight inline>stash</syntaxhighlight>''es''. |
Latest revision as of 15:23, 1 September 2022
Tutorial | |
---|---|
Title: | Git Tutorials |
Provider: | HPC.NRW
|
Contact: | tutorials@hpc.nrw |
Type: | Online |
Topic Area: | Revision control |
License: | CC-BY-SA |
Syllabus
| |
1. Basic Git overview | |
2. Creating and Changing Repositories | |
3. Branching |
Branching is an important feature of Git, which enables simultaneous work on different parts of a code with minimal interference between acting parties. As the name implies, branching refers to the creation of a separate line of repository tracking that branches of from your "main" repository (also called a branch, but can be understood as the trunk). When developing new features you do not necessarily want to directly commit
your changes to "main", because they might interfere with your colleagues contribution of other features on a different branch. Even when differences do not directly interfere, they can still lead to a more cumbersome merge
of both branches into "main", once all changes are complete. In larger projects this merge
ing into the "main" branch is often blocked for most users. A so-called pull
request must be made, so that a responsible person can pull
your changes from your branch and merge
them into "main".
To view all existing branches use the git branch --list
command:
1user@HPC.NRW:~$ git branch --list
2* main
We can see that currently the project contains only one branch called "main" (line 2). The "*" next to it is used to mark our current branch. Before we continue with the tutorial, a look at Figure 1 might be helpful. It visualizes the basic idea behind branching within a repository. Moreover, we can see the similarities between the relation of other branches to the main branch and the relation of a local repository to the remote repository.
Handling Branches
Creating a new branch is as easy as entering git branch <name of branch>
:
1user@HPC.NRW:~$ git branch branchingTut
2user@HPC.NRW:~$ git branch --list
3 branchingTut
4* main
5user@HPC.NRW:~$ git branch -a
6 branchingTut
7* main
8 remotes/origin/main
In above example we can see that creating a new branch does not imply switching to it (the "*" is still next to "main"). Further, displaying all local and remote branches through git branch -a
, we see that the new branch does only exist locally and not in the remote repository unlike "main" (line 8). The new branch will be identical to the last commit
of the current branch, in our case "main".
To switch to a different branch we use git checkout <name of branch>
:
1user@HPC.NRW:~$ git checkout branchingTut
2M someFile
3Switched to branch 'branchingTut'
4user@HPC.NRW:~$ git branch -a
5* branchingTut
6 main
7 remotes/origin/main
Line 2 informs us about a M
odified file named "someFile". This can happen if we make changes to our repository (like A
dding or M
odifying files), but do not commit
them before switching the branch. Naturally, this also happens when we want to switch to a branch with different contents than in hour current branch.
Keep in mind that a git checkout <name of branch>
is not like a git clone
operation and therefore does not make a full copy of a different code branch. Instead, it tracks the commit
history of the chosen branch and reproduces the changes in our repository. Any modifications we commit
now will be added to that branch.
To delete a branch we type git branch -d <name of branch>
. However, this will not be possible if the branch contains uncommitted changes. We either have to perform a commit
first or use the option -D
instead of -d
. If we need to restore a deleted branch we can use git checkout -b <new/old name of branch> <SHA of its last commit>
. The SHA-value has been touched upon, previously.
If we want to create a new branch and directly switch to it, we use git checkout -b <name of new branch> .
|
Usually, a branch is created from the most recent commit of the branch you are currently on. If we want to create it from a different branch (and switch to it), we will have to use git checkout -b <name of new branch> <name of different branch> .
|
Deleting a branch as shown above only deletes its local version. A remote branch has to be deleted this way: git push <name of remote repository> --delete <name of remote branch>
|
Renaming our current branch is done through the command git branch -m <new name of branch> .
|
Sometimes we want to switch to a different branch, but we have uncommit
ted changes in the current branch. In such a case, Git will not allow us to switch. If we do not want to perform a commit
we can use the git stash
functionality. With it we can safely put away all changes since the last commit
. Afterwards, we can restore what has been put into the stash with git stash pop
. Here is an example:
1otherUser@HPC.NRW:~$ git checkout main
2error: Your local changes to the following files would be overwritten by checkout:
3 Branching
4Please commit your changes or stash them before you switch branches.
5Aborting
6otherUser@HPC.NRW:~$ git stash
7Saved working directory and index state WIP on branchingTut: 802006b Add branch deletion to conflictless merge
8otherUser@HPC.NRW:~$ git checkout main
9Switched to branch 'main'
10Your branch is ahead of 'origin/main' by 15 commits.
11 (use "git push" to publish your local commits)
12otherUser@HPC.NRW:~$ git checkout branchingTut
13Switched to branch 'branchingTut'
14otherUser@HPC.NRW:~$ git stash pop
15On branch branchingTut
16Changes not staged for commit:
17 (use "git add <file>..." to update what will be committed)
18 (use "git restore <file>..." to discard changes in working directory)
19 modified: Branching
20
21no changes added to commit (use "git add" and/or "git commit -a")
22Dropped refs/stash@{0} (fe7bb4e2c792a6dcecd4e576a22a6640410cb078)
It is possible to have multiple stashes at once and even to apply them to other branches and much more. If you are interested we suggest Git's easy to understand section on working with stashes.
Remote Branches
If others are supposed to have access to our branch (or we want to have access from different locations), we need to make sure that it also exists in the remote repository by entering git push <name of remote repository> <name of branch>
:
1user@HPC.NRW:~$ git push origin branchingTut
2Username for 'https://github.com': HPC.NRW-User
3Password for 'https://HPC.NRW-User@github.com': ********
4#Enumerating objects: 15, done.
5Counting objects: 100% (15/15), done.
6Delta compression using up to 12 threads
7Compressing objects: 100% (13/13), done.
8Writing objects: 100% (13/13), 42.00 KiB | 10.50 MiB/s, done.
9Total 13 (delta 6), reused 0 (delta 0)
10remote: Resolving deltas: 100% (6/6), completed with 1 local object.
11remote:
12remote: Create a pull request for 'branchingTut' on GitHub by visiting:
13remote: https://github.com/HPC.NRW-User/Wikipages/pull/new/branchingTut
14remote:
15To https://github.com/HPC.NRW-User/Wikipages.git
16 * [new branch] branchingTut -> branchingTut
17
18user@HPC.NRW:~$ git branch -a
19* branchingTut
20 main
21 remotes/origin/branchingTut
22 remotes/origin/main
This is very similar to the previous push
operations with the only difference being that instead of the "main" branch in the remote repository, we will be using the "branchingTut" branch. As this branch did not exist yet, it will be created for us. In lines 18 to 22 we can see all existing branches including the newly created remote branch.
A remote checkout
works the same way a local checkout
does. The main difference is that your local repository might not be up to date with remote branch information, which is why we might miss updates on existing branches (as well as respective git log
data). Below, you can see Git output for a user who cloned our repository after the first tutorial, but before branching was introduced:
otherUser@HPC.NRW:~$ git branch -a
* main
remotes/origin/HEAD -> origin/main
remotes/origin/main
In order to see the newest changes in the remote repository, they need to use the fetch
command:
1otherUser@HPC.NRW:~$ git fetch
2Username for 'https://github.com': HPC.NRW-otherUser
3Password for 'https://HPC.NRW-otherUser@github.com':
4remote: Enumerating objects: 15, done.
5remote: Counting objects: 100% (15/15), done.
6remote: Compressing objects: 100% (7/7), done.
7remote: Total 13 (delta 6), reused 13 (delta 6), pack-reused 0
8Unpacking objects: 100% (13/13), done.
9From https://github.com/HPC.NRW-User/Wikipages
10 * [new branch] branchingTut -> origin/branchingTut
11
12otherUser@HPC.NRW:~$ git branch -a
13* main
14 remotes/origin/HEAD -> origin/main
15 remotes/origin/branchingTut
16 remotes/origin/main
Line 10 informs them that a new branch exists in the remote repository. Lines 12 to 16 show that they can see the "branchingTut" branch, however, they still do not have a local version of it. Using git checkout branchingTut
they can then create a local branch based on the remote one. As the branch already exists in the remote repository, the option -b
is not required for branch creation using checkout
.
It is possible to display updated remote repository information without actually updating:git ls-remote origin
|
git fetch <name of remote branch> can be used to only update information on one particular remote branch.
|
If more than one remote repository is available, git remote update will update information on all of them.
|
If we have several remote repositories, we need to further specify the desired branch: git checkout -b <name of branch> <name of remote repository>/<name of remote branch>
|
git checkout is actually more versatile and not only used for switching or restoring branches. Therefore, beginning with version 2.23, Git introduced git switch <name of existing branch> for switching to a different branch.
|
git switch -c <name of new branch> creates a new branch and switches to it.
|
If you switch to a local branch that has further developed than its remote version, Git will inform you:
otherUser@HPC.NRW:~$ git switch main
Switched to branch 'main'
Your branch is ahead of 'origin/main' by 2 commits.
(use "git push" to publish your local commits)
|
git switch is still marked as experimental in the Git documentation, which is why we only mention it here.
|
Merging Branches
Once work on a branch has progressed sufficiently, the changes can be merge
d into "main", i.e. that "main" will contain all changes and additions from the other branch. If the changes on the branch concern a separate file or new section in existing files, the merge
can be performed without any additional work. However, sometimes merge conflicts can occure, for example when two branches want to modify the same code sections or when "main" has changed after a branch has been created from it. We will cover both scenarios beginning with the easy one, where everything works fine.
Generally, git merge <name of branch to merge>
combines the given branch into our currently active branch, but first we need some content to merge
. For a conflict free merge
we will do the following:
- Switch to "main"
- Make sure it is locally up to date
- Create an new branch "tempBranch" out of "main"
- Create a new file "tempFile" and add it to "tempBranch"
- Merge "tempBranch" into "main", so that "tempFile" can also be found in "main"
- Write something into the first line of "tempFile" on "main"
- Write the same thing into the first line of "tempFile" on "tempBranch"
- Write something into the second line of "tempFile" on "tempBranch"
- Merge "tempBranch" into "main" again
- Remove "tempBranch"
Above, we have purposefully not mentioned any commit
operations. Try it yourself or check the solution below.
For the sake of improved readability we have added some blank lines in the commands and output below.
Remarks to some lines can be found at the bottom. 1user@HPC.NRW:~$ git switch main
2Switched to branch 'main'
3Your branch is up to date with 'origin/main'.
4user@HPC.NRW:~$ git checkout -b tempBranch
5Switched to a new branch 'tempBranch'
6
7user@HPC.NRW:~$ touch tempFile
8
9user@HPC.NRW:~$ git add tempFile
10
11user@HPC.NRW:~$ git commit -m "Add tempFile to tempBranch"
12[tempBranch b6bc099] Add tempFile to tempBranch
13 1 file changed, 0 insertions(+), 0 deletions(-)
14 create mode 100644 tempFile
15user@HPC.NRW:~$ git checkout main
16Switched to branch 'main'
17Your branch is up to date with 'origin/main'.
18
19user@HPC.NRW:~$ git merge -m "Merge tempBranch into main" tempBranch
20Updating 7ea3b70..b6bc099
21Fast-forward (no commit created; -m option ignored)
22 tempFile | 0
23 1 file changed, 0 insertions(+), 0 deletions(-)
24 create mode 100644 tempFile
25
26user@HPC.NRW:~$ echo "firstLine" >> tempFile
27user@HPC.NRW:~$ git add tempFile
28user@HPC.NRW:~$ git commit -m "Write in first line of tempFile"
29[main 2a4a1d6] Write in first line of tempFile
30 1 file changed, 1 insertion(+)
31
32user@HPC.NRW:~$ git switch tempBranch
33Switched to branch 'tempBranch'
34
35user@HPC.NRW:~$ touch tempFile; echo "firstLine" >> tempFile; echo "secondLine" >> tempFile; cat tempFile
36firstLine
37secondLine
38user@HPC.NRW:~$ git add tempFile ; git commit -m "Write in second line of tempFile"
39[tempBranch ee1bb34] Write in second line of tempFile
40 1 file changed, 3 insertions(+)
41
42user@HPC.NRW:~$ git switch main; cat tempFile
43Switched to branch 'main'
44Your branch is ahead of 'origin/main' by 2 commits.
45 (use "git push" to publish your local commits)
46firstLine
47
48user@HPC.NRW:~$ git merge -m "Merge with tempBranch without conflicts" tempBranch
49Merge made by the 'recursive' strategy.
50 tempFile | 1 +
51 1 file changed, 1 insertion(+)
52
53user@HPC.NRW:~$ cat tempFile
54firstLine
55secondLine
56
57user@HPC.NRW:~$ git log --oneline
5820e8d8f (HEAD -> main) Merge with tempBranch without conflicts
598aa439f (tempBranch) Write in second line of tempFile
60
61user@HPC.NRW:~$ git switch branchingTut
62Switched to branch 'branchingTut'
63user@HPC.NRW:~$ git branch -d tempBranch
64error: The branch 'tempBranch' is not fully merged.
65If you are sure you want to delete it, run 'git branch -D tempBranch'.
66
67user@HPC.NRW:~$ git switch main
68Switched to branch 'main'
69Your branch is ahead of 'origin/main' by 15 commits.
70 (use "git push" to publish your local commits)
71user@HPC.NRW:~$ git branch -d tempBranch
72Deleted branch tempBranch (was 8a4439f).
73
74user@HPC.NRW:~$ git branch -a
75 branchingTut
76 creatingAndChangingRepositoriesTut
77* main
78 remotes/origin/branchingTut
79 remotes/origin/creatingAndChangingRepositoriesTut
80 remotes/origin/main
|
Some remarks to the different lines:
|
Next, we will create a merge
with a conflict. As mentioned before, a conflict can occur when two users try to modify the same parts of a code, because it becomes unclear which changes have to be incorporated into the merge
and which not. To reproduce such a situation, we can mostly repeat the steps from the previous merge
without conflicts. Here are some instructions with a solution and remarks below:
- Create two branches "tempBranch" and "toMergeBranch" (does not matter of which branch)
- Create a file "tempFile" on both branches
- Write something in the first line of "tempFile" on "tempBranch"
- Write something in the second line of "tempFile" on "toMergeBranch"
- Merge "toMergeBranch" into "tempBranch" providing a merge message
- Resolve the conflict (see conflict solving basics below)
- Delete branches "tempBranch" and "toMergeBranch"
For the sake of improved readability we have added some blank lines in the commands and output below.
Remarks to some lines can be found at the bottom. 1user@HPC.NRW:~$ git branch -a
2* branchingTut
3 creatingAndChangingRepositoriesTut
4 main
5 remotes/origin/branchingTut
6 remotes/origin/creatingAndChangingRepositoriesTut
7 remotes/origin/main
8
9user@HPC.NRW:~$ git branch tempBranch
10user@HPC.NRW:~$ git branch toMergeBranch
11
12user@HPC.NRW:~$ git switch tempBranch
13M Branching
14Switched to branch 'tempBranch'
15
16user@HPC.NRW:~$ rm -r *
17
18user@HPC.NRW:~$ touch tempFile; echo "write in first line" >> tempFile
19user@HPC.NRW:~$ git add .
20user@HPC.NRW:~$ git commit -m "Delete some files in branch and write tempFile"
21[tempBranch 8385a37] Delete some files in branch and write tempFile
22 5 files changed, 1 insertion(+), 620 deletions(-)
23 delete mode 100644 Branching
24 delete mode 100644 Creating_and_Changing_Repositories
25 delete mode 100644 Git_Tutorials
26 delete mode 100644 clonedRepoFile
27 create mode 100644 tempFile
28
29user@HPC.NRW:~$ git switch toMergeBranch
30Switched to branch 'toMergeBranch'
31user@HPC.NRW:~$ rm -r *
32user@HPC.NRW:~$ touch tempFile; echo "" >> tempFile; echo "something in second line" >> tempFile
33user@HPC.NRW:~$ cat tempFile
34
35something in second line
36user@HPC.NRW:~$ git add .
37user@HPC.NRW:~$ git commit -m "Delete some files on branch and write tempFile second line"
38[toMergeBranch 529b936] Delete some files on branch and write tempFile second line
39 5 files changed, 2 insertions(+), 620 deletions(-)
40 delete mode 100644 Branching
41 delete mode 100644 Creating_and_Changing_Repositories
42 delete mode 100644 Git_Tutorials
43 delete mode 100644 clonedRepoFile
44 create mode 100644 tempFile
45
46user@HPC.NRW:~$ git switch tempBranch
47Switched to branch 'tempBranch'
48user@HPC.NRW:~$ git merge -m "merge toMergeBranch into tempBranch" toMergeBranch
49CONFLICT (add/add): Merge conflict in tempFile
50Auto-merging tempFile
51Automatic merge failed; fix conflicts and then commit the result.
52
53user@HPC.NRW:~$ ls .git/
54branches/ config FETCH_HEAD hooks/ info/ MERGE_HEAD MERGE_MSG ORIG_HEAD refs/
55COMMIT_EDITMSG description HEAD index logs/ MERGE_MODE objects/ packed-refs
56
57user@HPC.NRW:~$ vi tempFile
58user@HPC.NRW:~$ git add tempFile
59user@HPC.NRW:~$ git commit -m "Merge with toMergeBranch"
60[tempBranch 5e72b28] Merge with toMergeBranch
61
62user@HPC.NRW:~$ ls .git/
63branches/ config FETCH_HEAD hooks/ info/ objects/ packed-refs
64COMMIT_EDITMSG description HEAD index logs/ ORIG_HEAD refs/
65
66user@HPC.NRW:~$ cat tempFile
67write in first line
68something in second line
69
70user@HPC.NRW:~$ git switch branchingTut
71Switched to branch 'branchingTut'
72
73user@HPC.NRW:~$ git branch -d tempBranch
74error: The branch 'tempBranch' is not fully merged.
75If you are sure you want to delete it, run 'git branch -D tempBranch'.
76
77user@HPC.NRW:~$ git branch -D tempBranch
78Deleted branch tempBranch (was 5e72b28).
79user@HPC.NRW:~$ git branch -D toMergeBranch
80Deleted branch toMergeBranch (was 529b936).
|
Below we provide an example for a file containing a merge conflict. In this case, it is the file "tempFile" from the exercise above.
<<<<<<< HEAD
write in first line
=======
something in second line
>>>>>>> toMergeBranch
The <<<<<<< HEAD
marks the beginning of a difference or conflict in the file on our current branch (the branch we wanted to merge
into), =======
separates this content from the content in the branch we wanted to merge
. Therefore, everything between =======
and >>>>>>> toMergeBranch
is the corresponding content on branch "toMergeBranch". Anything outside of those markers is identical on both branches. Further, those markers can appear multiple times in one file, should several conflicts exist.
An easy way to check which files contain conflicts is to look into ./.git/MERGE_MSG
. This file is only present while a merge
conflict persists.
Best Practices
- Always start a new branch when working on a new feature and
merge
your changes and additions into the main branch, once the work is finished. - Common branches are: main, develop, feature, release, hotfix. Naturally, those branches can have branches as well, e.g., when multiple features are being developed at the same time. However, depending on your environment, completely different naming conventions can be sensible.
- Several strategies exist on the structured creation of branches and branching conventions. We cover them here.
Glossary
branch
: Used for branch operations (e.g., creation or deletion).switch
: Similar tocheckout
, but reserved for switching branches. New feature, so functionality might change.fetch
: Used to update information on remote repositories (and branches).merge
: Used to combine one branch into another.stash
: Used to temporarily store away uncommit
ted changes. Can be retrieved with agit stash pop
.
Useful Links
- Interactive GitHub course: Introduction and basic branch operations
- Paper on aspects of RSE: Section 3.2.3 contains information on different branching workflows and where they are employed.
- Git documentary: Further information on the possibilities of
stash
es.