From bc5a7e930ad29a502122d53f81c5593db22efb7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Tue, 21 Oct 2025 16:03:55 +0200 Subject: [PATCH 1/2] rebase example: add a new demo of the rebasing API This demonstrates the interesting bits of rebasing, i.e. what happens in the case of a conflict and how to abort. --- examples/common.h | 1 + examples/lg2.c | 1 + examples/rebase.c | 691 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 693 insertions(+) create mode 100644 examples/rebase.c diff --git a/examples/common.h b/examples/common.h index 901c04146..dc1ab7a81 100644 --- a/examples/common.h +++ b/examples/common.h @@ -73,6 +73,7 @@ extern int lg2_ls_files(git_repository *repo, int argc, char **argv); extern int lg2_ls_remote(git_repository *repo, int argc, char **argv); extern int lg2_merge(git_repository *repo, int argc, char **argv); extern int lg2_push(git_repository *repo, int argc, char **argv); +extern int lg2_rebase(git_repository *repo, int argc, char **argv); extern int lg2_remote(git_repository *repo, int argc, char **argv); extern int lg2_rev_list(git_repository *repo, int argc, char **argv); extern int lg2_rev_parse(git_repository *repo, int argc, char **argv); diff --git a/examples/lg2.c b/examples/lg2.c index 7946bc215..3107ab814 100644 --- a/examples/lg2.c +++ b/examples/lg2.c @@ -29,6 +29,7 @@ struct { { "ls-remote", lg2_ls_remote, 1 }, { "merge", lg2_merge, 1 }, { "push", lg2_push, 1 }, + { "rebase", lg2_rebase, 0 }, { "remote", lg2_remote, 1 }, { "rev-list", lg2_rev_list, 1 }, { "rev-parse", lg2_rev_parse, 1 }, diff --git a/examples/rebase.c b/examples/rebase.c new file mode 100644 index 000000000..bda6fe97e --- /dev/null +++ b/examples/rebase.c @@ -0,0 +1,691 @@ +/* + * libgit2 "rebase" example - shows how to use the rebase API + * + * Written by the libgit2 contributors + * + * To the extent possible under law, the author(s) have dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide. This software is distributed without any warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication along + * with this software. If not, see + * . + */ + +#include "common.h" +#include +#include + +#define REPO_PATH "test-repo" +#define CLONE_PATH "test-repo-clone" + +static void check_error(int error_code, const char *action) +{ + if (error_code < 0) { + const git_error *e = git_error_last(); + fprintf(stderr, "Error %d/%d: %s (%s)\n", error_code, e->klass, action, e->message); + exit(1); + } +} + +static void create_file(const char *repo_path, const char *filename, const char *content) +{ + char filepath[1024]; + FILE *file; + + snprintf(filepath, sizeof(filepath), "%s/%s", repo_path, filename); + file = fopen(filepath, "w"); + if (!file) { + fprintf(stderr, "Failed to create file: %s\n", filepath); + exit(1); + } + fprintf(file, "%s", content); + fclose(file); +} + +/* Global counter for timestamps to ensure consistent ordering */ +static int commit_timestamp = 1700000000; /* Base timestamp: Nov 14, 2023 */ + +static git_oid commit_file(git_repository *repo, const char *filename, const char *content, const char *message) +{ + git_oid tree_id, commit_id; + git_tree *tree; + git_index *index; + git_signature *sig; + git_reference *head_ref; + git_commit *parent = NULL; + + /* Create or update the file */ + const char *workdir = git_repository_workdir(repo); + create_file(workdir, filename, content); + + /* Add file to index */ + check_error(git_repository_index(&index, repo), "Failed to get index"); + check_error(git_index_add_bypath(index, filename), "Failed to add file to index"); + check_error(git_index_write(index), "Failed to write index"); + + /* Write the index as a tree */ + check_error(git_index_write_tree(&tree_id, index), "Failed to write tree"); + check_error(git_tree_lookup(&tree, repo, &tree_id), "Failed to lookup tree"); + + /* Create signature with hardcoded timestamp */ + check_error(git_signature_new(&sig, "Test User", "test@example.com", + commit_timestamp++, 0), "Failed to create signature"); + + /* Get parent commit if exists */ + if (git_repository_head(&head_ref, repo) == 0) { + git_object *head_obj; + check_error(git_reference_peel(&head_obj, head_ref, GIT_OBJECT_COMMIT), "Failed to peel HEAD"); + parent = (git_commit *)head_obj; + git_reference_free(head_ref); + } + + /* Create commit */ + if (parent) { + const git_commit *parents[] = { parent }; + check_error(git_commit_create( + &commit_id, repo, "HEAD", sig, sig, NULL, message, tree, 1, parents), + "Failed to create commit"); + git_commit_free(parent); + } else { + check_error(git_commit_create( + &commit_id, repo, "HEAD", sig, sig, NULL, message, tree, 0, NULL), + "Failed to create initial commit"); + } + + git_tree_free(tree); + git_signature_free(sig); + git_index_free(index); + + return commit_id; +} + +static void display_history(git_repository *repo, const char *title, int max_commits) +{ + git_revwalk *walker; + git_oid oid; + int count = 0; + + printf("\n%s:\n", title); + printf("----------------------------------------\n"); + + /* Create revision walker */ + check_error(git_revwalk_new(&walker, repo), "Failed to create revwalk"); + git_revwalk_push_head(walker); + git_revwalk_sorting(walker, GIT_SORT_TOPOLOGICAL | GIT_SORT_TIME); + + /* Walk through commits */ + while (git_revwalk_next(&oid, walker) == 0 && count < max_commits) { + git_commit *commit; + const char *message; + char oid_str[GIT_OID_MAX_HEXSIZE + 1]; + char msg_first_line[256]; + const char *newline; + size_t len; + + check_error(git_commit_lookup(&commit, repo, &oid), "Failed to lookup commit"); + message = git_commit_message(commit); + + /* Format OID as string */ + git_oid_tostr(oid_str, sizeof(oid_str), &oid); + + /* Print commit info - truncate message at newline */ + newline = strchr(message, '\n'); + if (newline) { + len = newline - message; + if (len > 255) len = 255; + strncpy(msg_first_line, message, len); + msg_first_line[len] = '\0'; + } else { + strncpy(msg_first_line, message, 255); + msg_first_line[255] = '\0'; + } + + printf(" %.7s %s\n", oid_str, msg_first_line); + + git_commit_free(commit); + count++; + } + + git_revwalk_free(walker); + printf("----------------------------------------\n"); +} + +static void create_initial_repository(const char *path) +{ + git_repository *repo = NULL; + char cmd[256]; + + printf("Creating repository at %s...\n", path); + + /* Remove existing repository if present */ + snprintf(cmd, sizeof(cmd), "rm -rf %s", path); + system(cmd); + + /* Initialize repository */ + check_error(git_repository_init(&repo, path, 0), "Failed to initialize repository"); + + /* Create initial commits */ + printf("Creating initial commits...\n"); + commit_file(repo, "README.md", "# Test Repository\n\nThis is a test repository for demonstrating rebasing.\n", "Initial commit"); + commit_file(repo, "file1.txt", "Content of file 1\nLine 2\nLine 3\n", "Add file1.txt"); + commit_file(repo, "file2.txt", "Content of file 2\nOriginal content\n", "Add file2.txt"); + + /* Display initial history */ + display_history(repo, "Initial repository history", 10); + + git_repository_free(repo); +} + +static void clone_repository(const char *source_path, const char *dest_path) +{ + git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT; + git_repository *cloned_repo = NULL; + char cmd[256]; + + printf("Cloning repository from %s to %s...\n", source_path, dest_path); + + /* Remove existing clone if present */ + snprintf(cmd, sizeof(cmd), "rm -rf %s", dest_path); + system(cmd); + + /* Clone the repository */ + check_error(git_clone(&cloned_repo, source_path, dest_path, &clone_opts), "Failed to clone repository"); + + git_repository_free(cloned_repo); +} + +static void create_divergent_commits(const char *repo1_path, const char *repo2_path) +{ + git_repository *repo1, *repo2; + + printf("\n=== Creating Divergent Commits ===\n"); + + /* Open repositories */ + check_error(git_repository_open(&repo1, repo1_path), "Failed to open repository 1"); + check_error(git_repository_open(&repo2, repo2_path), "Failed to open repository 2"); + + /* Create commits in repo1 */ + printf("\nCreating commits in original repository (%s)...\n", repo1_path); + /* This will conflict on line 2 only */ + commit_file(repo1, "file1.txt", "Content of file 1\nLine 2 changed in repo1\nLine 3\nNew line 4 added in repo1\n", + "Modify file1.txt in repo1"); + commit_file(repo1, "file3.txt", "New file 3 from repo1\n", "Add file3.txt in repo1"); + /* This will conflict on the second line */ + commit_file(repo1, "file2.txt", "Content of file 2\nModified by repo1\nExtra content from repo1\n", + "Update file2.txt in repo1"); + + /* Display repo1 history */ + display_history(repo1, "Original repository history after divergent commits", 10); + + /* Create commits in repo2 (clone) */ + printf("\nCreating commits in cloned repository (%s)...\n", repo2_path); + /* This will conflict on line 2 only */ + commit_file(repo2, "file1.txt", "Content of file 1\nLine 2 modified in repo2\nLine 3\nLine 4 from repo2\n", + "Change file1.txt in repo2"); + commit_file(repo2, "file4.txt", "New file 4 from repo2\n", "Add file4.txt in repo2"); + /* This will conflict on the second line */ + commit_file(repo2, "file2.txt", "Content of file 2\nChanged by repo2\nDifferent ending\n", + "Modify file2.txt differently in repo2"); + + /* Display repo2 history */ + display_history(repo2, "Cloned repository history after divergent commits", 10); + + git_repository_free(repo1); + git_repository_free(repo2); +} + +static void fetch_from_upstream(git_repository *repo, const char *upstream_path) +{ + git_remote *remote; + git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT; + + printf("Fetching from upstream...\n"); + + /* Create a remote pointing to the upstream */ + if (git_remote_lookup(&remote, repo, "upstream") != 0) { + check_error(git_remote_create(&remote, repo, "upstream", upstream_path), + "Failed to create upstream remote"); + } + + /* Fetch from upstream */ + check_error(git_remote_fetch(remote, NULL, &fetch_opts, NULL), "Failed to fetch from upstream"); + + git_remote_free(remote); +} + + +static char* read_blob_content(git_repository *repo, const git_oid *oid) +{ + git_blob *blob; + char *content; + size_t size; + + if (git_blob_lookup(&blob, repo, oid) != 0) { + return strdup("(unable to read content)"); + } + + size = git_blob_rawsize(blob); + content = malloc(size + 1); + if (content) { + memcpy(content, git_blob_rawcontent(blob), size); + content[size] = '\0'; + } + + git_blob_free(blob); + return content; +} + +/* Split content into lines for comparison */ +static char** split_lines(const char *content, int *line_count) +{ + int capacity = 16; + int count = 0; + char **lines = malloc(capacity * sizeof(char*)); + const char *start = content; + const char *end; + int len; + + while (*start) { + end = strchr(start, '\n'); + if (!end) { + end = start + strlen(start); + } + + len = end - start; + if (count >= capacity) { + capacity *= 2; + lines = realloc(lines, capacity * sizeof(char*)); + } + + lines[count] = malloc(len + 2); + memcpy(lines[count], start, len); + if (*end == '\n') { + lines[count][len] = '\n'; + lines[count][len + 1] = '\0'; + } else { + lines[count][len] = '\0'; + } + count++; + + if (*end == '\n') { + start = end + 1; + } else { + break; + } + } + + *line_count = count; + return lines; +} + +static void free_lines(char **lines, int count) +{ + int i; + for (i = 0; i < count; i++) { + free(lines[i]); + } + free(lines); +} + +static void write_line_by_line_merge(FILE *file, const char *ours_content, const char *theirs_content) +{ + int ours_count, theirs_count; + char **ours_lines = split_lines(ours_content, &ours_count); + char **theirs_lines = split_lines(theirs_content, &theirs_count); + int max_lines = ours_count > theirs_count ? ours_count : theirs_count; + int in_conflict = 0; + int conflict_start = -1; + int i, j; + const char *ours_line; + const char *theirs_line; + int same; + + for (i = 0; i < max_lines; i++) { + ours_line = i < ours_count ? ours_lines[i] : NULL; + theirs_line = i < theirs_count ? theirs_lines[i] : NULL; + + /* Check if lines are the same */ + same = 0; + if (ours_line && theirs_line) { + same = (strcmp(ours_line, theirs_line) == 0); + } else { + same = (ours_line == NULL && theirs_line == NULL); + } + + if (same) { + /* Lines match - close any open conflict and write the line */ + if (in_conflict) { + /* Write all accumulated conflict lines */ + fprintf(file, "<<<<<<< HEAD (ours - current rebase state)\n"); + for (j = conflict_start; j < i; j++) { + if (j < ours_count && ours_lines[j]) { + fprintf(file, "%s", ours_lines[j]); + } + } + fprintf(file, "=======\n"); + for (j = conflict_start; j < i; j++) { + if (j < theirs_count && theirs_lines[j]) { + fprintf(file, "%s", theirs_lines[j]); + } + } + fprintf(file, ">>>>>>> upstream (incoming change)\n"); + in_conflict = 0; + conflict_start = -1; + } + if (ours_line) { + fprintf(file, "%s", ours_line); + } + } else { + /* Lines differ - mark start of conflict if not already in one */ + if (!in_conflict) { + in_conflict = 1; + conflict_start = i; + } + /* Don't write anything yet - accumulate the conflict */ + } + } + + /* Close any remaining conflict */ + if (in_conflict) { + fprintf(file, "<<<<<<< HEAD (ours - current rebase state)\n"); + for (j = conflict_start; j < max_lines; j++) { + if (j < ours_count && ours_lines[j]) { + fprintf(file, "%s", ours_lines[j]); + } + } + fprintf(file, "=======\n"); + for (j = conflict_start; j < max_lines; j++) { + if (j < theirs_count && theirs_lines[j]) { + fprintf(file, "%s", theirs_lines[j]); + } + } + fprintf(file, ">>>>>>> upstream (incoming change)\n"); + } + + free_lines(ours_lines, ours_count); + free_lines(theirs_lines, theirs_count); +} + +static void handle_rebase_conflict(git_repository *repo, git_rebase *rebase) +{ + git_index *index; + git_index_conflict_iterator *conflicts; + const git_index_entry *ancestor, *ours, *theirs; + int has_conflicts = 0; + + printf(" Handling conflicts...\n"); + + /* Get the index */ + check_error(git_repository_index(&index, repo), "Failed to get index"); + + /* Check for conflicts */ + check_error(git_index_conflict_iterator_new(&conflicts, index), "Failed to create conflict iterator"); + + while (git_index_conflict_next(&ancestor, &ours, &theirs, conflicts) == 0) { + has_conflicts = 1; + printf(" Conflict in file: %s\n", ours ? ours->path : (theirs ? theirs->path : "unknown")); + + /* Create a file with actual conflict markers from the real content */ + if (ours && theirs) { + char filepath[1024]; + FILE *file; + const char *workdir = git_repository_workdir(repo); + char *ours_content = NULL; + char *theirs_content = NULL; + char *ancestor_content = NULL; + + /* Read the actual blob contents */ + ours_content = read_blob_content(repo, &ours->id); + theirs_content = read_blob_content(repo, &theirs->id); + if (ancestor) { + ancestor_content = read_blob_content(repo, &ancestor->id); + } + + snprintf(filepath, sizeof(filepath), "%s/%s", workdir, ours->path); + file = fopen(filepath, "w"); + if (file) { + /* Create line-by-line merge with partial conflict markers */ + write_line_by_line_merge(file, ours_content, theirs_content); + fclose(file); + + printf(" Created partial conflict markers (only conflicting lines)\n"); + + /* Mark as resolved by adding to index */ + git_index_add_bypath(index, ours->path); + } + + free(ours_content); + free(theirs_content); + free(ancestor_content); + } + } + + git_index_conflict_iterator_free(conflicts); + + if (has_conflicts) { + git_signature *sig; + git_oid commit_id; + + /* Write the index */ + check_error(git_index_write(index), "Failed to write index"); + + /* Continue rebase with resolved conflicts */ + check_error(git_signature_new(&sig, "Test User", "test@example.com", + commit_timestamp++, 0), "Failed to create signature"); + check_error(git_rebase_commit(&commit_id, rebase, NULL, sig, NULL, NULL), "Failed to commit during rebase"); + git_signature_free(sig); + } + + git_index_free(index); +} + +static void demonstrate_rebase_abort(const char *repo_path, const char *upstream_path) +{ + git_repository *repo; + git_rebase *rebase; + git_reference *upstream_ref; + git_annotated_commit *upstream_commit; + git_rebase_options rebase_opts = GIT_REBASE_OPTIONS_INIT; + git_signature *sig; + + printf("\n=== Demonstrating Rebase Abort ===\n"); + + /* Open repository */ + check_error(git_repository_open(&repo, repo_path), "Failed to open repository"); + + /* Show history before rebase */ + display_history(repo, "Clone repository history before rebase", 10); + + /* Fetch from upstream */ + fetch_from_upstream(repo, upstream_path); + + /* Get upstream branch reference */ + check_error(git_reference_lookup(&upstream_ref, repo, "refs/remotes/upstream/master"), + "Failed to lookup upstream/master"); + check_error(git_annotated_commit_from_ref(&upstream_commit, repo, upstream_ref), + "Failed to get annotated commit"); + + /* Create signature with hardcoded timestamp */ + check_error(git_signature_new(&sig, "Test User", "test@example.com", + commit_timestamp++, 0), "Failed to create signature"); + + /* Initialize rebase */ + printf("\nInitiating rebase onto upstream/master...\n"); + check_error(git_rebase_init(&rebase, repo, NULL, upstream_commit, NULL, &rebase_opts), + "Failed to initialize rebase"); + + /* Process first commit */ + { + git_rebase_operation *operation; + int error; + git_oid commit_id; + + error = git_rebase_next(&operation, rebase); + if (error == 0) { + printf("Processing first rebase operation...\n"); + printf(" Commit being rebased: %s\n", git_oid_tostr_s(&operation->id)); + + /* Actually commit the first operation */ + error = git_rebase_commit(&commit_id, rebase, NULL, sig, NULL, NULL); + if (error == 0) { + printf(" First commit successfully rebased as: %s\n", git_oid_tostr_s(&commit_id)); + } + + /* Try to process second commit */ + error = git_rebase_next(&operation, rebase); + if (error == 0) { + printf("\nProcessing second rebase operation...\n"); + printf(" Commit being rebased: %s\n", git_oid_tostr_s(&operation->id)); + printf(" (This operation will be aborted)\n"); + } + } + } + + /* Show in-progress rebase state */ + printf("\nRebase is in progress. Current HEAD is detached.\n"); + + /* Abort the rebase */ + printf("\nAborting rebase mid-operation...\n"); + check_error(git_rebase_abort(rebase), "Failed to abort rebase"); + printf("Rebase aborted successfully.\n"); + + /* Show history after abort - should be back to original */ + display_history(repo, "Clone repository history after abort (restored to original)", 10); + + /* Cleanup */ + git_signature_free(sig); + git_rebase_free(rebase); + git_annotated_commit_free(upstream_commit); + git_reference_free(upstream_ref); + git_repository_free(repo); +} + +static void demonstrate_successful_rebase(const char *repo_path, const char *upstream_path) +{ + git_repository *repo; + git_rebase *rebase; + git_reference *upstream_ref; + git_annotated_commit *upstream_commit; + git_rebase_options rebase_opts = GIT_REBASE_OPTIONS_INIT; + git_rebase_operation *operation; + git_signature *sig; + int error; + + printf("\n=== Demonstrating Successful Rebase with Conflict Resolution ===\n"); + + /* Open repository */ + check_error(git_repository_open(&repo, repo_path), "Failed to open repository"); + + /* Show history before rebase */ + display_history(repo, "Clone repository history before successful rebase", 10); + + /* Create signature with hardcoded timestamp */ + check_error(git_signature_new(&sig, "Test User", "test@example.com", + commit_timestamp++, 0), "Failed to create signature"); + + /* Fetch from upstream */ + fetch_from_upstream(repo, upstream_path); + + /* Get upstream branch reference */ + check_error(git_reference_lookup(&upstream_ref, repo, "refs/remotes/upstream/master"), + "Failed to lookup upstream/master"); + check_error(git_annotated_commit_from_ref(&upstream_commit, repo, upstream_ref), + "Failed to get annotated commit"); + + /* Initialize rebase */ + printf("Initiating rebase onto upstream/master...\n"); + check_error(git_rebase_init(&rebase, repo, NULL, upstream_commit, NULL, &rebase_opts), + "Failed to initialize rebase"); + + /* Process each rebase operation */ + while ((error = git_rebase_next(&operation, rebase)) == 0) { + git_oid commit_id; + git_index *index; + + printf("Processing rebase operation %zu...\n", git_rebase_operation_current(rebase)); + printf(" Commit: %s\n", git_oid_tostr_s(&operation->id)); + + /* Check for conflicts */ + check_error(git_repository_index(&index, repo), "Failed to get index"); + + if (git_index_has_conflicts(index)) { + printf(" Conflicts detected!\n"); + handle_rebase_conflict(repo, rebase); + } else { + /* No conflicts, proceed with commit */ + error = git_rebase_commit(&commit_id, rebase, NULL, sig, NULL, NULL); + if (error < 0 && error != GIT_EUNMERGED) { + check_error(error, "Failed to commit during rebase"); + } else if (error == GIT_EUNMERGED) { + printf(" Unmerged changes detected, handling...\n"); + handle_rebase_conflict(repo, rebase); + } else { + printf(" Successfully rebased commit: %s\n", git_oid_tostr_s(&commit_id)); + } + } + + git_index_free(index); + } + + if (error == GIT_ITEROVER) { + /* Finish the rebase */ + printf("\nFinishing rebase...\n"); + check_error(git_rebase_finish(rebase, sig), "Failed to finish rebase"); + printf("Rebase completed successfully!\n"); + + /* Display final history after successful rebase */ + display_history(repo, "Clone repository history after successful rebase", 10); + } else { + check_error(error, "Error during rebase"); + } + + /* Cleanup */ + git_signature_free(sig); + git_rebase_free(rebase); + git_annotated_commit_free(upstream_commit); + git_reference_free(upstream_ref); + git_repository_free(repo); +} + +/** + * This example demonstrates the libgit2 rebase APIs when faced with + * a conflict. It also shows how to handle aborting a rebase operation. + * + * This does not have: + * + * - Robust error handling + * - Interactive rebase options (pick, reword, squash, fixup) + * - Complex conflict resolution strategies + * + */ +int lg2_rebase(git_repository *repo, int argc, char **argv) +{ + UNUSED(repo); + UNUSED(argc); + UNUSED(argv); + + printf("=== libgit2 Rebase API Demonstration ===\n\n"); + + /* Step 1: Create initial repository with commits */ + create_initial_repository(REPO_PATH); + + /* Step 2: Clone the repository */ + clone_repository(REPO_PATH, CLONE_PATH); + + /* Step 3: Create divergent commits in both repositories */ + create_divergent_commits(REPO_PATH, CLONE_PATH); + + /* Step 4a: Demonstrate aborting a rebase */ + demonstrate_rebase_abort(CLONE_PATH, REPO_PATH); + + /* Step 4b: Demonstrate successful rebase with conflict resolution */ + demonstrate_successful_rebase(CLONE_PATH, REPO_PATH); + + printf("\n=== Demonstration Complete ===\n"); + printf("Repositories created at:\n"); + printf(" Original: %s\n", REPO_PATH); + printf(" Clone: %s\n", CLONE_PATH); + + return 0; +} From 54d142d9866bf0ca4a2b44ee89c30e4edf4306b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Tue, 21 Oct 2025 16:07:02 +0200 Subject: [PATCH 2/2] remote example: fix problem.strings[] indexing --- examples/remote.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/remote.c b/examples/remote.c index 14fac030c..9f988210c 100644 --- a/examples/remote.c +++ b/examples/remote.c @@ -80,7 +80,7 @@ int lg2_remote(git_repository *repo, int argc, char *argv[]) static int cmd_add(git_repository *repo, struct remote_opts *o) { char *name, *url; - git_remote *remote = {0}; + git_remote *remote = NULL; if (o->argc != 2) usage("you need to specify a name and URL", NULL); @@ -126,7 +126,7 @@ static int cmd_rename(git_repository *repo, struct remote_opts *o) return 0; for (i = 0; i < (int) problems.count; i++) { - puts(problems.strings[0]); + puts(problems.strings[i]); } git_strarray_dispose(&problems); @@ -172,7 +172,7 @@ static int cmd_show(git_repository *repo, struct remote_opts *o) const char *arg, *name, *fetch, *push; int verbose = 0; git_strarray remotes = {0}; - git_remote *remote = {0}; + git_remote *remote = NULL; for (i = 0; i < o->argc; i++) { arg = o->argv[i];