Git(二)

作者: 浅墨入画 | 来源:发表于2021-05-08 10:24 被阅读0次

一. 静态库符号冲突解决

1.1 链接同名静态库会不会冲突?

首先打开第一份工程LGTestApp,导入了AFNetworking

platform :ios, '14.0'
target 'LGTestApp' do
  # use_frameworks!
  pod 'AFNetworking'
end

接下来往工程中Frameworks目录下导入AFNetworking_static目录下的libAFNetworking.a

温馨小提示:

Pods/Pods-LGTestApp.debug.xcconfig配置文件中的
OTHER_LDFLAGS = $(inherited) -all_load -l"AFNetworking" 工程链接静态库中所有代码
改成默认OTHER_LDFLAGS = $(inherited) -ObjC -l"AFNetworking",工程链接静态库的方式会变成,链接所有与OC相关的类或者分类到当前工程ipa包中。

此时cocoapods链接了一个AFNetworking库,手动又拖进去一个AFNetworking库,Cmd + R会不会报冲突?
不会报错,cocoapods正常通过三要素进行链接,第一点通过HEADER_SEARCH_PATHS找到api,第二点通过LIBRARY_SEARCH_PATHS找到路径,最后通过OTHER_LDFLAGS根据库的名称链接库。当把一个库拖入Frameworks目录下,此时链接库的方式是通过静态库的路径进行链接。相当于OTHER_LDFLAGS = $(inherited) -ObjC -l"AFNetworking" /Users/wn/Documents/资料/上课代码/01-静态库冲突原因/LGTestApp/libAFNetworking.a,这个时候链接器发现静态库同名,会优先链接第一个库,第二个库就不会链接。

1.2 链接同名静态库报冲突的情况?

修改AFNetworking_static目录下的libAFNetworking.a名称为libAFNetworking2.a,导入Frameworks目录下,链接器会认为是两个不同的静态库,这个时候相同的静态库起不同的名字就会冲突。此时报了duplicate symbols for architecture x86_64符号冲突,重复定义符号。

OC为什么是一门动态语言?

当把代码编译到MachO之后,MachO中有一个__Data段,这个段的__Object_section中存放着所有与类相关的内容。当把MachO加载到内存时,Runtime会把section读出来,生成相应的Class_RO,Class_RW结构体,动态化的初始化对象。

下面我们创建一个单符号冲突的情况?
创建AFNetworking2静态库,静态库内容如下

// AFNetworking.h内容
#import <Foundation/Foundation.h>
void global_function() {
}
@interface AFURLSessionManager : NSObject 
@property (readonly, nonatomic, strong) NSURLSession *session;
@end

// AFNetworking.m内容
#import "AFNetworking.h"
@implementation AFURLSessionManager
@end

创建完静态库之后,把静态库工程添加到LGTestApp

image.png

把创建的静态库AFNetworking2添加到Frameworks下

image.png

编译之后报错如下

image.png
  • 命令一查看符号
$ tty
/dev/ttys003

// 修改AFNetworking2 静态库中的RunCMD.Config.xcconfig内容如下
// 详细输出终端命令
VERBOSE_SCRIPT_LOGGING=-v
MACH_PATH=${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/libAFNetworking2.a
MAP_PATH=${SRCROOT}/cat.m
CMD = nm -pa ${MAP_PATH}
TTY=/dev/ttys003

AFNetworking2静态库中RunCMD.Config.xcconfig配置完成之后,Cmd + R 运行,终端打印出符号如下

image.png

S表示存放在其他session,也就是__Data __ObjC的session段中.

  • 命令二查看符号,配置RunCMD.Config.xcconfig 内容如下
// 详细输出终端命令
VERBOSE_SCRIPT_LOGGING=-v
MACH_PATH=${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/libAFNetworking2.a
MAP_PATH=${SRCROOT}/cat.m
CMD = objdump --macho -t ${MACH_PATH}
TTY=/dev/ttys003

Cmd + R 运行,终端打印出符号如下

image.png
小结
  • 只有全局符号才会产生冲突
解决方法
  • 方法一 文件产生冲突,可以使用ar命令对静态库进行解压缩,去掉重复符号,再包装成静态库
  • 方法二 符号产生冲突,上面产生冲突的符号AFURLSessionManager在不同文件中
    1.如果能够获取到源码,直接修改其中一个符号名称即可解决,如CT_AFURLSessionManager
    2.如果获取不到源码,可以对单独的库指定一些参数,如OTHER_LDFLAGS = $(inherited) -force_load "库路径" -l"AFNetworking",如果库比较多的话,需要对每一个库进行指定,虽然能解决问题,但是比较麻烦
    3.获取不到源码,推荐使用objcopy,直接对冲突的符号添加前缀或后缀,以此修改符号
$ llvm-objcopy --prefix-symbols=Cat...
image.png

我们发现llvm-objcopy命令并不支持MachO文件格式,可以使用下面命令

$ llvm-objcopy --redefine-sym "_OBJC_CLASS_$_AFURLSessionManager"="Cat__OBJC_CLASS_$_AFURLSessionManager"...
image.png image.png

成功修改第一个符号的前缀,接下来只需要把后面两个符号前缀修改,即可解决符号冲突
$ nm -gUAj .a路径 显示.a文件内所有的全局符号

image.png

此时把libAFNetworking2.a导入Frameworks目录下,Cmd + B编译成功

二. 解决符号冲突原因&脚本

上面我们把符号_OBJC_CLASS_$_AFURLSessionManager 改成了Cat_OBJC_CLASS_$_AFURLSessionManagerCat_OBJC_CLASS_$_AFURLSessionManager已经不属于OC的符号了,因为前缀发生了变化,底层已经解析不出来了。所以OTHER_LDFLAGS = $(inherited) -ObjC -l"AFNetworking"链接器在去链接AFNetworking2的时候,发现不是OC的符号,编译器会把Cat_OBJC_CLASS_$_AFURLSessionManagerstrip掉。

image.png

搜索可执行文件LGTestAppCat的符号,并没有找到,确定编译器把Cat_OBJC_CLASS_$_AFURLSessionManagerstrip掉了

image.png

修改OTHER_LDFLAGS = $(inherited) -all_load -l"AFNetworking"发现定义的全局符号global_function以及包含Cat的符号都存在。

思考一下

上面的静态库AFNetworking2中符号比较少,只报了三个符号冲突,当冲突的符号比较多时,我们不能一个个的添加前缀或后缀,那么该怎么解决呢?

  • 方法一 首先查看libAFNetworking.a静态库的全局符号
$ nm -gUAj libAFNetworking.a路径
image.png

发现每个符号所在路径并不相同,我们只需要对符号拼接前缀,放入原本的路径即可,所以我们可以编写脚本使用正则表达式来实现

// main.py脚本内容如下
import subprocess
import re  # 正则相关的方法

# path表示路径,使用nm命令把该路径的静态库符号输出
def getSymbols(path):
    # 在当前进程中执行命令输出静态库的符号
    # f表示格式化字符串
    return subprocess.getoutput(f'nm -gUAj {path}') 
    # 也可以这样写 return subprocess.getoutput("nm -gUAj {}".format(path))
    
# 修改完成之后输出到新的路径,把上图中 ‘ ___destory_helper_block_e8_32s’前面的路径设置为空,这里只需要替换一下
def writeNewSymbolsToFile(syms, path, newPath)  
    # r表示单引号里面是一个正则表达式,下面的正则表达式就能完整匹配到‘ ___destory_helper_block_e8_32s’前面的路径
    newStr = re.sub(rf'{path}[:](.+)(.o: )', '', syms)   
    # 把上面字符串按照换行符分割成数组
    lines = newStr.splitlines()
    newSys = ''
    for s in lines:
        # 去掉字符串中的换行符
        word = s.strip('\n')
        newSys += word + ' ' + f'_Cat{word}' + '\n' 
        print(newSys)

    # 打开newPath路径,默认创建w文件
    with open(newPath, 'w') as file:
        # 文件写入newPath
        file.write(newSys)
image.png
_OBJC_CLASS_$_AFAutoPurgingImageCache _Cat_OBJC_CLASS_$_AFAutoPurgingImageCache
_OBJC_CLASS_$_AFCachedImage _Cat_OBJC_CLASS_$_AFCachedImage
_OBJC_IVAR_$_AFAutoPurgingImageCache._cachedImages _Cat_OBJC_IVAR_$_AFAutoPurgingImageCache._cachedImages
...

我们发现符号名称成功修改,此时Cmd + B编译成功

  • 方法二 现在是两个不同名字,相同符号的静态库,可以把一个静态库改成动态库,就可以解决符号冲突的问题
platform :ios, '14.0'

target 'LGTestApp' do
  use_frameworks!
  pod 'AFNetworking'
end

三. Git集中式开发

首先我们来创建一个远程仓库,桌面创建目录远程仓库并进入,在远程仓库目录下创建两个目录CT.Remote HK.Remote

// 进入CT.Remote目录,初始化远程仓库
$ cd /Users/wn/Desktop/远程仓库/CT.Remote
// 初始化Git仓库,初始化完成之后,其他地方的代码就可以push到这个仓库
$ git init --bare
Initialized empty Git repository in /Users/wn/Desktop/远程仓库/CT.Remote/

// 进入HK.Remote目录,初始化远程仓库
$ cd /Users/wn/Desktop/远程仓库/HK.Remote
$ git init --bare
Initialized empty Git repository in /Users/wn/Desktop/远程仓库/HK.Remote/

接下来在桌面创建集中式开发目录,并在里面分别创建两个目录CT HK,在CT目录里面创建空工程LGClass

// LGClass工程中的ViewController.swift类中打印几句话
import UIKit
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        print("Hello Cat!")
        print("Hello Dog!")
    }
}

然后使用git命令分别初始化环境

$ cd /Users/wn/Desktop/集中式开发/CT
$ git init
Initialized empty Git repository in /Users/wn/Desktop/集中式开发/CT/.git/
// 查看是否添加过远程仓库
$ git remote
// 在当前目录添加一个远程仓库,origin给远程仓库起别名
$ git remote add origin /Users/wn/Desktop/远程仓库/CT.Remote
// 查看到远程仓库origin
$ git remote
origin
// 再添加一个远程仓库
$ git remote add originHK /Users/wn/Desktop/远程仓 库/HK.Remote
// 查看到有两个远程仓库,意味着当前CT目录下的代码可以往push到两个远程仓库
$ git remote
origin
originHK
// 添加到暂存区并提交代码
$ git add .
$ git commit -m 'init commit'
[master (root-commit) a4ca154] init commit
 16 files changed, 730 insertions(+)
 create mode 100644 LGClass/LGClass.xcodeproj/project.pbxproj
 create mode 100644 LGClass/LGClass.xcodeproj/project.xcworkspace/contents.xcworkspacedata
 create mode 100644 LGClass/LGClass.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
 create mode 100644 LGClass/LGClass.xcodeproj/project.xcworkspace/xcuserdata/wn.xcuserdatad/UserInterfaceState.xcuserstate
 create mode 100644 
...
// 查看当前分支
$ git branch
* master
// push到指定的远程仓库,下面报错提示没有指定push到远程仓库的哪个分支
$ git push origin
fatal: The current branch master has no upstream branch.
To push the current branch and set the remote as upstream, use
    git push --set-upstream origin master
// push到origin远程仓库的master分支
$ git push --set-upstream origin master
Enumerating objects: 32, done.
Counting objects: 100% (32/32), done.
Delta compression using up to 4 threads
Compressing objects: 100% (28/28), done.
Writing objects: 100% (32/32), 23.48 KiB | 3.35 MiB/s, done.
Total 32 (delta 2), reused 0 (delta 0)
To /Users/wn/Desktop/远程仓库/CT.Remote
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
// 进入远程仓库查看提交
$ cd /Users/wn/Desktop/远程仓库/CT.Remote
$ git log
commit a4ca154e8956919d3ee63392788258859afc3dc8 (HEAD -> master)
Author: wn <chris.wang@pingcoo.com>
Date:   Sat May 8 22:25:07 2021 +0800
    init commit

// 提交到远程仓库originHK的master分支
$ cd /Users/wn/Desktop/集中式开发/CT
$ git push --set-upstream originHK master
Enumerating objects: 32, done.
Counting objects: 100% (32/32), done.
Delta compression using up to 4 threads
Compressing objects: 100% (28/28), done.
Writing objects: 100% (32/32), 23.48 KiB | 2.94 MiB/s, done.
Total 32 (delta 2), reused 0 (delta 0)
To /Users/wn/Desktop/远程仓库/HK.Remote
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'originHK'.

现在想要在集中式开发 -> HK目录下也提交代码,操作如下

$ cd /Users/wn/Desktop/集中式开发/HK
$ git init
Initialized empty Git repository in /Users/wn/Desktop/集中式开发/HK/.git/
$ git remote add -t master origin /Users/wn/Desktop/远程仓库/CT.Remote
// 成功从远程仓库origin中拉下来代码
$ git pull
remote: Enumerating objects: 32, done.
remote: Counting objects: 100% (32/32), done.
remote: Compressing objects: 100% (28/28), done.
remote: Total 32 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (32/32), done.
From /Users/wn/Desktop/远程仓库/CT.Remote
 * [new branch]      master     -> origin/master
Fast-forwrod merge:

CT目录下成功拉下来LGClass工程代码,现在修改代码如下

// LGClass工程中的ViewController.swift类中打印几句话
import UIKit
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        print("Hello Cat!")
        print("Hello Dog!")
        print("Hello Tiger!")
    }
}

$ cd /Users/wn/Desktop/集中式开发/CT
$ git add .
$ git commit -m 'add tiger'
[master 122e218] add tiger
 2 files changed, 1 insertion(+), 5 deletions(-)
 rewrite LGClass/LGClass.xcodeproj/project.xcworkspace/xcuserdata/wn.xcuserdatad/UserInterfaceState.xcuserstate (76%)
// 成功提交一次
$ git push
Enumerating objects: 19, done.
Counting objects: 100% (19/19), done.
Delta compression using up to 4 threads
Compressing objects: 100% (9/9), done.
Writing objects: 100% (10/10), 6.14 KiB | 3.07 MiB/s, done.
Total 10 (delta 4), reused 0 (delta 0)
To /Users/wn/Desktop/远程仓库/HK.Remote
   a4ca154..122e218  master -> master
// 查看远程提交记录,发现远程master分支记录线条是直的,这就是单人开发Fast-forwrod merge形式
$ git log --oneline --decorate --graph --stat
* 761a65c (HEAD -> master) add tiger
|  .../UserInterfaceState.xcuserstate           | Bin 13336 -> 16644 bytes
|  LGClass/LGClass/ViewController.swift         |   6 +-----
|  2 files changed, 1 insertion(+), 5 deletions(-)
* a4ca154 (origin/master) init commit
   LGClass/LGClass.xcodeproj/project.pbxproj         | 344 ++++++++++++++++++++
   .../project.xcworkspace/contents.xcworkspacedata  |   7 +
   .../xcshareddata/IDEWorkspaceChecks.plist         |   8 +
   .../UserInterfaceState.xcuserstate                | Bin 0 -> 13336 bytes
   .../ws.xcuserdatad/UserInterfaceState.xcuserstate | Bin 0 -> 15456 bytes
   .../xcschemes/xcschememanagement.plist            |  14 +
   .../xcschemes/xcschememanagement.plist            |  14 +
   LGClass/LGClass/AppDelegate.swift                 |  36 ++
   .../AccentColor.colorset/Contents.json            |  11 +
   .../AppIcon.appiconset/Contents.json              |  98 ++++++
   LGClass/LGClass/Assets.xcassets/Contents.json     |   6 +
   .../LGClass/Base.lproj/LaunchScreen.storyboard    |  25 ++
   LGClass/LGClass/Base.lproj/Main.storyboard        |  24 ++
   LGClass/LGClass/Info.plist                        |  66 ++++
   LGClass/LGClass/SceneDelegate.swift               |  52 +++
   LGClass/LGClass/ViewController.swift              |  25 ++
   16 files changed, 730 insertions(+)

// 也可以用glola的全写形式查看提交记录,发现远程master分支记录线条是直的,这就是单人开发Fast-forwrod merge形式
$ git log --graph --pretty='%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --all
* 122e218 - (HEAD -> master, originHK/master) add tiger (15 minutes ago) <wn>
* a4ca154 - (origin/master) init commit (54 minutes ago) <wn>
image.png image.png
  • Fast-forwrod merge: 绿色表示远程分支,蓝色表示提交到远程的本地分支,push的时候这俩分支需要进行合并,这时发现远程分支后面并没有新的提交,那么直接把本地提交的代码合并到远程分支后面,这就是Fast-forwrod merge
多人开发
import UIKit
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        print("Hello Cat!")
        print("Hello Dog!")
        print("Hello Elephant!")
    }
}

$ cd /Users/wn/Desktop/集中式开发/HK
$ git add .
$ git commit -m 'add Elephant'
[master bd5950d] add Elephant
 2 files changed, 5 insertions(+), 1 deletion(-)
 rewrite LGClass/LGClass.xcodeproj/project.xcworkspace/xcuserdata/wn.xcuserdatad/UserInterfaceState.xcuserstate (94%)
$ git log --oneline --decorate --graph --stat
* bd5950d (HEAD -> master) add Elephant
|  .../UserInterfaceState.xcuserstate           | Bin 16644 -> 16909 bytes
|  LGClass/LGClass/ViewController.swift         |   6 +++++-
|  2 files changed, 5 insertions(+), 1 deletion(-)
* a4ca154 (origin/master) init commit
   LGClass/LGClass.xcodeproj/project.pbxproj         | 344 ++++++++++++++++++++
   .../project.xcworkspace/contents.xcworkspacedata  |   7 +
   .../xcshareddata/IDEWorkspaceChecks.plist         |   8 +
   .../UserInterfaceState.xcuserstate                | Bin 0 -> 13336 bytes
   .../ws.xcuserdatad/UserInterfaceState.xcuserstate | Bin 0 -> 15456 bytes
   .../xcschemes/xcschememanagement.plist            |  14 +
   .../xcschemes/xcschememanagement.plist            |  14 +
   LGClass/LGClass/AppDelegate.swift                 |  36 ++
   .../AccentColor.colorset/Contents.json            |  11 +
   .../AppIcon.appiconset/Contents.json              |  98 ++++++
   LGClass/LGClass/Assets.xcassets/Contents.json     |   6 +
   .../LGClass/Base.lproj/LaunchScreen.storyboard    |  25 ++
   LGClass/LGClass/Base.lproj/Main.storyboard        |  24 ++
   LGClass/LGClass/Info.plist                        |  66 ++++
   LGClass/LGClass/SceneDelegate.swift               |  52 +++
   LGClass/LGClass/ViewController.swift              |  25 ++
   16 files changed, 730 insertions(+)
// 此时push到远程会报错,提示我们远程分支有了新的提交,在push之前要先git pull 下来代码
$ git push --set-upstream origin master
![rejected]         master -> master (fetch first)
error: failed to push some refs to /Users/wn/Desktop/远程仓库/HK.Remote
hint: Updates were rejected because the remote contains work that you do 
hint: (e.g.,’git pull …’) before pushing again
... 
// 拉代码的时候产生了冲突
$ git pull origin master
From /Users/wn/Desktop/远程仓库/HK.Remote
 * branch       master      -> FETCH_HEAD
Auto-merging LGClass/LGClass/ViewController.swift
CONFLICT (content): Merge conflict in LGClass/LGClass/ViewController.swift
Automatic merge failed; fix conflict and then commit the result.

// 此时产生了冲突
import UIKit
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        print("Hello Cat!")
        print("Hello Dog!")
<<<<<<<  HEAD
        print("Hello Tiger!")
=======
        print("Hello Elephant!")
>>>>>>> CommitID
    }
}

// 手动解决完上面冲突之后提交代码
$ git commit -m 'new merge' 
image.png
  • git pull 有两个命令,一个git fetch 一个git merge
  • git fetch 之后本地有两个master分支,一个远程一个本地。
截屏2021-05-09 上午12.15.02.png
// 此时查看远程提交记录,发现远程master分支多了个分支记录
$ git log --oneline --decorate --graph --stat
// 下面我们来把上面的merge多出来的线条撸直
// 回到commit之前的状态,也就是上一个HEAD
$ git rev-parse ORIG_HEAD
761a65cbef6d7bbe6403c3c7aac5051eb70ecc93
// 这时就恢复到git pull拉取远程分支之前的状态
$ git reset --hard ORIG_HEAD
// 表示当前分支要添加到远程分支后面
$ git pull origin master --rebase
// 查看冲突解决冲突
$ git mergetool
// 添加到暂存区,并且添加到远程分支后面
$ git add .
$ git rebase --continue
// 此时查看远程提交记录,发现远程master分支成功撸直
$ git log --oneline --decorate --graph --stat
// 成功解决
$ git push 

// 举例
// 表示当前分支与dog分支进行合并,merge与rebase刚好相反
$ git merge dog
image1.png image2.png image3.png image4.png image5.png image6.png image7.png
  • rebase 不是创建一个新的commit,而是将指定的分支放到另一个分支的结尾
小提示

分支上面保存的是最后一次提交,并不是所有的提交,branch本质上表示当前分支的最后一次提交,本地的dev分支提交到远程的dev分支,会发生一次合并

相关文章

网友评论

      本文标题:Git(二)

      本文链接:https://www.haomeiwen.com/subject/uwdmrltx.html